From 84f5f39bfccefb58b39b7be691990243e9a16d82 Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Thu, 11 Mar 2021 15:56:27 -0800 Subject: [PATCH 001/127] Working UI and some functions --- ...oft.CodeAnalysis.EditorFeatures.Wpf.csproj | 1 + .../Commands/PredefinedCommandHandlerNames.cs | 5 + .../ValueTracking/IValueTrackingService.cs | 17 ++ .../ValueTracking/ValueTrackedItem.cs | 26 +++ .../ValueTracking/ValueTrackingService.cs | 135 ++++++++++++++ src/VisualStudio/Core/Def/Commands.vsct | 19 ++ .../DataFlowWindow/DataFlowCommandHandler.cs | 37 ++++ .../DataFlowEditorCommandArgs.cs | 22 +++ .../Def/DataFlowWindow/DataFlowToolWindow.cs | 24 +++ src/VisualStudio/Core/Def/Guids.cs | 3 + .../Core/Def/ID.RoslynCommands.cs | 1 + .../Def/Implementation/CommandBindings.cs | 5 + ...osoft.VisualStudio.LanguageServices.csproj | 5 +- .../Core/Def/PackageRegistration.pkgdef | 6 + src/VisualStudio/Core/Def/RoslynPackage.cs | 21 ++- .../ValueTrackingCommandHandler.cs | 174 ++++++++++++++++++ .../ValueTrackingEditorCommandArgs.cs | 22 +++ .../ValueTracking/ValueTrackingToolWindow.cs | 58 ++++++ .../Def/ValueTracking/ValueTrackingTree.xaml | 72 ++++++++ .../ValueTracking/ValueTrackingTree.xaml.cs | 35 ++++ .../ValueTrackingTreeItemViewModel.cs | 163 ++++++++++++++++ .../ValueTrackingTreeViewModel.cs | 37 ++++ .../Core/Def/xlf/Commands.vsct.cs.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.de.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.es.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.fr.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.it.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.ja.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.ko.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.pl.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.pt-BR.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.ru.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.tr.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.zh-Hans.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.zh-Hant.xlf | 15 ++ 35 files changed, 1075 insertions(+), 8 deletions(-) create mode 100644 src/Features/Core/Portable/ValueTracking/IValueTrackingService.cs create mode 100644 src/Features/Core/Portable/ValueTracking/ValueTrackedItem.cs create mode 100644 src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs create mode 100644 src/VisualStudio/Core/Def/DataFlowWindow/DataFlowCommandHandler.cs create mode 100644 src/VisualStudio/Core/Def/DataFlowWindow/DataFlowEditorCommandArgs.cs create mode 100644 src/VisualStudio/Core/Def/DataFlowWindow/DataFlowToolWindow.cs create mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs create mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingEditorCommandArgs.cs create mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs create mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml create mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs create mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs create mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs diff --git a/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj b/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj index 4211ba99baff2..19a867ba77c57 100644 --- a/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj +++ b/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj @@ -69,6 +69,7 @@ + diff --git a/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.cs b/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.cs index 545e53524b7d7..531132d451939 100644 --- a/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.cs +++ b/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.cs @@ -178,5 +178,10 @@ internal static class PredefinedCommandHandlerNames /// Command handler name for Edit and Continue file save handler. /// public const string EditAndContinueFileSave = "Edit and Continue Save File Handler"; + + /// + /// Command handler name for showing the Value Tracking tool window. + /// + public const string ShowValueTracking = "Show Value Tracking"; } } diff --git a/src/Features/Core/Portable/ValueTracking/IValueTrackingService.cs b/src/Features/Core/Portable/ValueTracking/IValueTrackingService.cs new file mode 100644 index 0000000000000..8504d153808f9 --- /dev/null +++ b/src/Features/Core/Portable/ValueTracking/IValueTrackingService.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; + +namespace Microsoft.CodeAnalysis.ValueTracking +{ + internal interface IValueTrackingService : IWorkspaceService + { + Task> TrackValueSourceAsync(Solution solution, Location location, ISymbol symbol, CancellationToken cancellationToken); + Task> TrackValueSourceAsync(Solution solution, ValueTrackedItem previousTrackedItem, CancellationToken cancellationToken); + } +} diff --git a/src/Features/Core/Portable/ValueTracking/ValueTrackedItem.cs b/src/Features/Core/Portable/ValueTracking/ValueTrackedItem.cs new file mode 100644 index 0000000000000..46247c807d43f --- /dev/null +++ b/src/Features/Core/Portable/ValueTracking/ValueTrackedItem.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.ValueTracking +{ + internal class ValueTrackedItem + { + public Location Location { get; } + public ISymbol Symbol { get; } + public SyntaxNode? ExpressionNode { get; } + public ValueTrackedItem? PreviousTrackedItem { get; } + + public ValueTrackedItem( + Location location, + ISymbol symbol, + SyntaxNode? expressionNode = null, + ValueTrackedItem? previousTrackedItem = null) + { + Location = location; + Symbol = symbol; + ExpressionNode = expressionNode; + PreviousTrackedItem = previousTrackedItem; + } + } +} diff --git a/src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs b/src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs new file mode 100644 index 0000000000000..581c63ada04a7 --- /dev/null +++ b/src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs @@ -0,0 +1,135 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.FindSymbols.Finders; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServices; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ValueTracking +{ + [ExportWorkspaceService(typeof(IValueTrackingService)), Shared] + internal class ValueTrackingService : IValueTrackingService + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public ValueTrackingService() + { + } + + public async Task> TrackValueSourceAsync( + Solution solution, + Location location, + ISymbol symbol, + CancellationToken cancellationToken) + { + switch (symbol) + { + case IPropertySymbol: + case ILocalSymbol: + case IFieldSymbol: + { + var assignments = await TrackAssignmentsAsync(solution, symbol, cancellationToken).ConfigureAwait(false); + return assignments.Select(a => new ValueTrackedItem(a.Location.Location, symbol)).ToImmutableArray(); + } + + default: + return ImmutableArray.Empty; + } + } + + public async Task> TrackValueSourceAsync( + Solution solution, + ValueTrackedItem previousTrackedItem, + CancellationToken cancellationToken) + { + RoslynDebug.AssertNotNull(previousTrackedItem.Location.SourceTree); + + if (previousTrackedItem.PreviousTrackedItem is null) + { + var symbol = previousTrackedItem.Symbol; + var assignments = await TrackAssignmentsAsync(solution, symbol, cancellationToken).ConfigureAwait(false); + return assignments.Select(a => new ValueTrackedItem(a.Location.Location, symbol, previousTrackedItem: previousTrackedItem)).ToImmutableArray(); + } + + // There's no interesting node + if (previousTrackedItem.ExpressionNode is null) + { + return ImmutableArray.Empty; + } + + if (previousTrackedItem.Symbol is IPropertySymbol) + { + var document = solution.GetRequiredDocument(previousTrackedItem.Location.SourceTree); + return await TrackFromPropertyAssignmentAsync(solution, document, previousTrackedItem, cancellationToken).ConfigureAwait(false); + } + + throw new Exception(); + } + + private static async Task> TrackAssignmentsAsync( + Solution solution, + ISymbol symbol, + CancellationToken cancellationToken) + { + using var _ = PooledObjects.ArrayBuilder.GetInstance(out var builder); + var projectsToSearch = await ReferenceFinders.Property.DetermineProjectsToSearchAsync(symbol, solution, solution.Projects.ToImmutableHashSet(), cancellationToken).ConfigureAwait(false); + foreach (var project in projectsToSearch) + { + var documentsToSearch = await ReferenceFinders.Property.DetermineDocumentsToSearchAsync( + symbol, + project, + project.Documents.ToImmutableHashSet(), + FindReferencesSearchOptions.Default, + cancellationToken).ConfigureAwait(false); + + foreach (var document in documentsToSearch) + { + var syntaxFacts = document.GetRequiredLanguageService(); + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var referencesInDocument = await ReferenceFinders.Property.FindReferencesInDocumentAsync( + symbol, + document, + semanticModel, + FindReferencesSearchOptions.Default, + cancellationToken).ConfigureAwait(false); + + builder.AddRange(referencesInDocument + .Where(r => r.Location.IsWrittenTo)); + } + } + + return builder.AsImmutableOrEmpty(); + } + + private static async Task> TrackFromPropertyAssignmentAsync(Solution solution, Document document, ValueTrackedItem valueTrackedItem, CancellationToken cancellationToken) + { + RoslynDebug.AssertNotNull(valueTrackedItem.ExpressionNode); + + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var symbolInfo = semanticModel.GetSymbolInfo(valueTrackedItem.ExpressionNode); + + if (symbolInfo.Symbol is not null) + { + var assignments = await TrackAssignmentsAsync(solution, symbolInfo.Symbol, cancellationToken).ConfigureAwait(false); + return assignments + .Select(a => new ValueTrackedItem(a.Location.Location, symbolInfo.Symbol, previousTrackedItem: valueTrackedItem)) + .ToImmutableArray(); + } + + return ImmutableArray.Empty; + } + } +} diff --git a/src/VisualStudio/Core/Def/Commands.vsct b/src/VisualStudio/Core/Def/Commands.vsct index 422b93c7d0bfd..591fab85c9ba0 100644 --- a/src/VisualStudio/Core/Def/Commands.vsct +++ b/src/VisualStudio/Core/Def/Commands.vsct @@ -411,6 +411,19 @@ RemoveUnusedReferences + + @@ -516,6 +529,10 @@ + + + + @@ -601,6 +618,8 @@ + + diff --git a/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowCommandHandler.cs b/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowCommandHandler.cs new file mode 100644 index 0000000000000..cf53b8a500771 --- /dev/null +++ b/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowCommandHandler.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.ComponentModel.Composition; +using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.VisualStudio.Commanding; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.VisualStudio.LanguageServices.DataFlowWindow +{ + [Export(typeof(ICommandHandler))] + [ContentType(ContentTypeNames.RoslynContentType)] + [Name(PredefinedCommandHandlerNames.GoToDataFlow)] + internal class DataFlowCommandHandler : ICommandHandler + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public DataFlowCommandHandler() + { + } + + public string DisplayName => "Go to data flow"; + + public bool ExecuteCommand(DataFlowEditorCommandArgs args, CommandExecutionContext executionContext) + { + throw new NotImplementedException(); + } + + public CommandState GetCommandState(DataFlowEditorCommandArgs args) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowEditorCommandArgs.cs b/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowEditorCommandArgs.cs new file mode 100644 index 0000000000000..64d0a85908b5b --- /dev/null +++ b/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowEditorCommandArgs.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Editor.Commanding; + +namespace Microsoft.VisualStudio.LanguageServices.DataFlowWindow +{ + internal class DataFlowEditorCommandArgs : EditorCommandArgs + { + public DataFlowEditorCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer) + { + } + } +} diff --git a/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowToolWindow.cs b/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowToolWindow.cs new file mode 100644 index 0000000000000..325c47176f9d1 --- /dev/null +++ b/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowToolWindow.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; +using System.Windows.Controls; +using Microsoft.VisualStudio.Shell; + +namespace Microsoft.VisualStudio.LanguageServices.DataFlowWindow +{ + [Guid("60a19d42-2dd7-43f3-be90-c7a9cb7d28f4")] + internal class DataFlowToolWindow : ToolWindowPane + { + public DataFlowToolWindow() + { + this.Caption = "Data Flow Tool Window"; + Content = new TextBlock() + { + Text = "Testing" + }; + } + } +} diff --git a/src/VisualStudio/Core/Def/Guids.cs b/src/VisualStudio/Core/Def/Guids.cs index 6f058e3c50002..994acb0fbbebc 100644 --- a/src/VisualStudio/Core/Def/Guids.cs +++ b/src/VisualStudio/Core/Def/Guids.cs @@ -125,6 +125,9 @@ internal static class Guids public static readonly Guid RoslynCommandSetId = new(RoslynCommandSetIdString); public static readonly Guid RoslynGroupId = new(RoslynGroupIdString); + public const string ValueTrackingToolWindowIdString = "60a19d42-2dd7-43f3-be90-c7a9cb7d28f4"; + public static readonly Guid ValueTrackingToolWindowId = new(ValueTrackingToolWindowIdString); + // TODO: Remove pending https://github.com/dotnet/roslyn/issues/8927 . // Interactive guids public const string InteractiveCommandSetIdString = "00B8868B-F9F5-4970-A048-410B05508506"; diff --git a/src/VisualStudio/Core/Def/ID.RoslynCommands.cs b/src/VisualStudio/Core/Def/ID.RoslynCommands.cs index d89285327286e..ad65fd8e87b1f 100644 --- a/src/VisualStudio/Core/Def/ID.RoslynCommands.cs +++ b/src/VisualStudio/Core/Def/ID.RoslynCommands.cs @@ -50,6 +50,7 @@ public static class RoslynCommands public const int RunCodeAnalysisForProject = 0x0201; public const int RemoveUnusedReferences = 0x0202; + public const int GoToValueTrackingWindow = 0x0203; } } } diff --git a/src/VisualStudio/Core/Def/Implementation/CommandBindings.cs b/src/VisualStudio/Core/Def/Implementation/CommandBindings.cs index 27a6efccbd0ea..b2992efa119ab 100644 --- a/src/VisualStudio/Core/Def/Implementation/CommandBindings.cs +++ b/src/VisualStudio/Core/Def/Implementation/CommandBindings.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.Editor.Commanding.Commands; using Microsoft.VisualStudio.Editor.Commanding; using Microsoft.VisualStudio.LanguageServices; +using Microsoft.VisualStudio.LanguageServices.ValueTracking; namespace Microsoft.VisualStudio.Editor.Implementation { @@ -28,5 +29,9 @@ internal sealed class CommandBindings [Export] [CommandBinding(Guids.CSharpGroupIdString, ID.CSharpCommands.ContextOrganizeRemoveAndSort, typeof(SortAndRemoveUnnecessaryImportsCommandArgs))] internal CommandBindingDefinition contextOrganizeRemoveAndSortCommandBinding; + + [Export] + [CommandBinding(Guids.RoslynGroupIdString, ID.RoslynCommands.GoToValueTrackingWindow, typeof(ValueTrackingEditorCommandArgs))] + internal CommandBindingDefinition gotoDataFlowToolCommandBinding; } } diff --git a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj index 5fab552e80282..1eb93e319b038 100644 --- a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj +++ b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj @@ -33,7 +33,7 @@ 6cf2e545-6109-4730-8883-cf43d7aec3e1 - + @@ -199,8 +199,7 @@ - + diff --git a/src/VisualStudio/Core/Def/PackageRegistration.pkgdef b/src/VisualStudio/Core/Def/PackageRegistration.pkgdef index 27e41b380e9ba..d1b8281f52747 100644 --- a/src/VisualStudio/Core/Def/PackageRegistration.pkgdef +++ b/src/VisualStudio/Core/Def/PackageRegistration.pkgdef @@ -24,3 +24,9 @@ "Value"=dword:00000000 "Title"="Enable experimental C#/VB LSP completion experience" "PreviewPaneChannels"="IntPreview,int.main" + +// The option page configuration is duplicated in RoslynPackage +// [ProvideToolWindow(typeof(ValueTracking.ValueTrackingToolWindow))] +[$RootKey$\ToolWindows\{60a19d42-2dd7-43f3-be90-c7a9cb7d28f4}] +"Name"="Microsoft.VisualStudio.LanguageServices.ValueTracking.ValueTrackingToolWindow" +@="{6cf2e545-6109-4730-8883-cf43d7aec3e1}" diff --git a/src/VisualStudio/Core/Def/RoslynPackage.cs b/src/VisualStudio/Core/Def/RoslynPackage.cs index 7261ce5c45d59..57e373566a571 100644 --- a/src/VisualStudio/Core/Def/RoslynPackage.cs +++ b/src/VisualStudio/Core/Def/RoslynPackage.cs @@ -19,28 +19,23 @@ using Microsoft.CodeAnalysis.Logging; using Microsoft.CodeAnalysis.Notification; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.CodeAnalysis.Versions; using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.LanguageServices.ColorSchemes; using Microsoft.VisualStudio.LanguageServices.Experimentation; using Microsoft.VisualStudio.LanguageServices.Implementation; -using Microsoft.VisualStudio.LanguageServices.Implementation.DesignerAttribute; using Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics; using Microsoft.VisualStudio.LanguageServices.Implementation.Interactive; using Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.RuleSets; -using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectTelemetry; using Microsoft.VisualStudio.LanguageServices.Implementation.TableDataSource; -using Microsoft.VisualStudio.LanguageServices.Implementation.TodoComments; using Microsoft.VisualStudio.LanguageServices.Implementation.UnusedReferences; using Microsoft.VisualStudio.LanguageServices.Telemetry; using Microsoft.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -using Microsoft.VisualStudio.Shell.ServiceBroker; using Microsoft.VisualStudio.TaskStatusCenter; using Microsoft.VisualStudio.Telemetry; using Microsoft.VisualStudio.TextManager.Interop; @@ -51,6 +46,9 @@ namespace Microsoft.VisualStudio.LanguageServices.Setup { [Guid(Guids.RoslynPackageIdString)] + + // The option page configuration is duplicated in PackageRegistration.pkgdef + [ProvideToolWindow(typeof(ValueTracking.ValueTrackingToolWindow))] internal sealed class RoslynPackage : AbstractPackage { // The randomly-generated key name is used for serializing the ILSpy decompiler EULA preference to the .SUO @@ -188,6 +186,19 @@ protected override async Task LoadComponentsAsync(CancellationToken cancellation LoadComponentsBackgroundAsync(cancellationToken).Forget(); } + // Overrides for VSSDK003 fix + // See https://github.com/Microsoft/VSSDK-Analyzers/blob/main/doc/VSSDK003.md + public override IVsAsyncToolWindowFactory GetAsyncToolWindowFactory(Guid toolWindowType) + => toolWindowType == typeof(ValueTracking.ValueTrackingToolWindow).GUID + ? this + : base.GetAsyncToolWindowFactory(toolWindowType); + + protected override string GetToolWindowTitle(Type toolWindowType, int id) + => base.GetToolWindowTitle(toolWindowType, id); + + protected override Task InitializeToolWindowAsync(Type toolWindowType, int id, CancellationToken cancellationToken) + => Task.FromResult((object?)null); + private async Task LoadComponentsBackgroundAsync(CancellationToken cancellationToken) { await TaskScheduler.Default; diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs new file mode 100644 index 0000000000000..2bcbb7f1463eb --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs @@ -0,0 +1,174 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.ComponentModel.Composition; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.Editor.Shared.Extensions; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServices; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.ValueTracking; +using Microsoft.VisualStudio.Commanding; +using Microsoft.VisualStudio.GraphModel.CodeSchema; +using Microsoft.VisualStudio.LanguageServices.Setup; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Utilities; +using IAsyncServiceProvider = Microsoft.VisualStudio.Shell.IAsyncServiceProvider; +using Task = System.Threading.Tasks.Task; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + [Export(typeof(ICommandHandler))] + [ContentType(ContentTypeNames.RoslynContentType)] + [Name(PredefinedCommandHandlerNames.ShowValueTracking)] + internal class ValueTrackingCommandHandler : ICommandHandler + { + private readonly IAsyncServiceProvider _serviceProvider; + private readonly IThreadingContext _threadingContext; + + private RoslynPackage? _roslynPackage; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public ValueTrackingCommandHandler(SVsServiceProvider serviceProvider, IThreadingContext threadingContext) + { + _serviceProvider = (IAsyncServiceProvider)serviceProvider; + _threadingContext = threadingContext; + } + + public string DisplayName => "Go to value tracking"; + + public CommandState GetCommandState(ValueTrackingEditorCommandArgs args) + => CommandState.Available; + + public bool ExecuteCommand(ValueTrackingEditorCommandArgs args, CommandExecutionContext executionContext) + { + var cancellationToken = executionContext.OperationContext.UserCancellationToken; + var caretPosition = args.TextView.GetCaretPoint(args.SubjectBuffer); + if (!caretPosition.HasValue) + { + return false; + } + + var textSpan = new TextSpan(caretPosition.Value.Position, 0); + var sourceTextContainer = args.SubjectBuffer.AsTextContainer(); + var document = sourceTextContainer.GetOpenDocumentInCurrentContext(); + if (document is null) + { + return false; + } + + var selectedSymbol = _threadingContext.JoinableTaskFactory.Run(() => GetSelectedSymbolAsync(textSpan, document, cancellationToken)); + if (selectedSymbol is null) + { + return false; + } + + var syntaxTree = document.GetRequiredSyntaxTreeSynchronously(cancellationToken); + var location = Location.Create(syntaxTree, textSpan); + + _threadingContext.JoinableTaskFactory.Run(() => ShowToolWindowAsync(selectedSymbol, location, document.Project.Solution, cancellationToken)); + + return true; + } + + private static async Task GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) + { + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var selectedNode = root.FindNode(textSpan); + if (selectedNode is null) + { + return null; + } + + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var selectedSymbol = + semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol + ?? semanticModel.GetDeclaredSymbol(selectedNode, cancellationToken); + + if (selectedSymbol is null) + { + return null; + } + + return selectedSymbol switch + { + ILocalSymbol + or IPropertySymbol { SetMethod: not null } + or IFieldSymbol { IsReadOnly: false } + or IEventSymbol + or IParameterSymbol + => selectedSymbol, + + _ => null + }; + } + + private async Task ShowToolWindowAsync(ISymbol selectedSymbol, Location location, Solution solution, CancellationToken cancellationToken) + { + var roslynPackage = await TryGetRoslynPackageAsync(cancellationToken).ConfigureAwait(false); + + if (roslynPackage is null) + { + return; + } + + var dataFlowItem = new ValueTrackingTreeItemViewModel( + new ValueTrackedItem(location, selectedSymbol), + solution, + solution.Workspace.Services.GetRequiredService()); + + if (ValueTrackingToolWindow.Instance is null) + { + var factory = roslynPackage.GetAsyncToolWindowFactory(Guids.ValueTrackingToolWindowId); + + factory.CreateToolWindow(Guids.ValueTrackingToolWindowId, 0, dataFlowItem); + await factory.InitializeToolWindowAsync(Guids.ValueTrackingToolWindowId, 0); + + ValueTrackingToolWindow.Instance = (ValueTrackingToolWindow)await roslynPackage.ShowToolWindowAsync( + typeof(ValueTrackingToolWindow), + 0, + true, + roslynPackage.DisposalToken).ConfigureAwait(false); + } + else + { + ValueTrackingToolWindow.Instance.Root = dataFlowItem; + await roslynPackage.ShowToolWindowAsync( + typeof(ValueTrackingToolWindow), + 0, + false, + roslynPackage.DisposalToken).ConfigureAwait(false); + } + } + + private async ValueTask TryGetRoslynPackageAsync(CancellationToken cancellationToken) + { + if (_roslynPackage is null) + { + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var shell = (IVsShell7?)await _serviceProvider.GetServiceAsync(typeof(SVsShell)).ConfigureAwait(true); + Assumes.Present(shell); + await shell.LoadPackageAsync(typeof(RoslynPackage).GUID); + + if (ErrorHandler.Succeeded(((IVsShell)shell).IsPackageLoaded(typeof(RoslynPackage).GUID, out var package))) + { + _roslynPackage = (RoslynPackage)package; + } + } + + return _roslynPackage; + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingEditorCommandArgs.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingEditorCommandArgs.cs new file mode 100644 index 0000000000000..75e7930090a3a --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingEditorCommandArgs.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Editor.Commanding; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class ValueTrackingEditorCommandArgs : EditorCommandArgs + { + public ValueTrackingEditorCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer) + { + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs new file mode 100644 index 0000000000000..67c24e9cf3d5e --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Controls; +using Microsoft.CodeAnalysis; +using Microsoft.VisualStudio.Shell; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + [Guid(Guids.ValueTrackingToolWindowIdString)] + internal class ValueTrackingToolWindow : ToolWindowPane + { + public static ValueTrackingToolWindow? Instance { get; set; } + private readonly ValueTrackingTreeViewModel _viewModel; + + // Needed for VSSDK003 + // See https://github.com/Microsoft/VSSDK-Analyzers/blob/main/doc/VSSDK003.md + public ValueTrackingToolWindow(object o) + : base(null) + { + if (Instance is not null) + { + throw new Exception("Cannot initialize the window more than once"); + } + + this.Caption = "Value Tracking"; + + if (o is ValueTrackingTreeItemViewModel root) + { + _viewModel = new ValueTrackingTreeViewModel(root); + Content = new ValueTrackingTree(_viewModel); + } + else + { + throw new Exception("This shouldn't happen"); + } + } + + public ValueTrackingTreeItemViewModel Root + { + get => _viewModel.Roots.Single(); + set + { + _viewModel.Roots.Clear(); + _viewModel.Roots.Add(value); + } + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml new file mode 100644 index 0000000000000..2b19c1bce37bd --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs new file mode 100644 index 0000000000000..87ee81bb63c8b --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + /// + /// Interaction logic for ValueTrackingTree.xaml + /// + internal partial class ValueTrackingTree : UserControl + { + private readonly ValueTrackingTreeViewModel _viewModel; + + public ValueTrackingTree(ValueTrackingTreeViewModel viewModel) + { + DataContext = _viewModel = viewModel; + InitializeComponent(); + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs new file mode 100644 index 0000000000000..a82eb4936d020 --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs @@ -0,0 +1,163 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.ValueTracking; +using Roslyn.Utilities; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class TreeViewItemBase : INotifyPropertyChanged + { + private bool _isExpanded = false; + public bool IsNodeExpanded + { + get => _isExpanded; + set => SetProperty(ref _isExpanded, value); + } + + private bool _isSelected = false; + public bool IsNodeSelected + { + get => _isSelected; + set => SetProperty(ref _isSelected, value); + } + + public event PropertyChangedEventHandler? PropertyChanged; + + protected void SetProperty(ref T field, T value, [CallerMemberName] string name = "") + { + if (EqualityComparer.Default.Equals(field, value)) + { + return; + } + + field = value; + NotifyPropertyChanged(name); + } + + protected void NotifyPropertyChanged(string name) + => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + } + internal class EmptyTreeViewItem : TreeViewItemBase + { + public static EmptyTreeViewItem Instance { get; } = new(); + + private EmptyTreeViewItem() + { + } + } + + internal class ValueTrackingTreeItemViewModel : TreeViewItemBase + { + private bool _childrenCalculated; + private readonly Solution _solution; + private readonly IValueTrackingService _valueTrackingService; + + public ObservableCollection ChildItems { get; } = new(); + + public Location Location => TrackedItem.Location; + public ISymbol Symbol => TrackedItem.Symbol; + + public string FileName => Path.GetFileName(Location.SourceTree!.FilePath); + public string LineDisplay + { + get + { + var linePosition = Location.GetLineSpan().StartLinePosition; + return linePosition.Line.ToString(); + } + } + + public string SymbolName => Symbol.ToDisplayString(); + public string ContainerName => Symbol.ContainingSymbol.ToDisplayString(); + + public ValueTrackedItem TrackedItem { get; } + + public ValueTrackingTreeItemViewModel( + ValueTrackedItem trackedItem, + Solution solution, + IValueTrackingService valueTrackingService) + { + Contract.ThrowIfFalse(trackedItem.Location.IsInSource); + + ChildItems.Add(EmptyTreeViewItem.Instance); + + TrackedItem = trackedItem; + _solution = solution; + _valueTrackingService = valueTrackingService; + + ChildItems.CollectionChanged += (s, a) => + { + NotifyPropertyChanged(nameof(ChildItems)); + }; + + PropertyChanged += (s, a) => + { + if (a.PropertyName == nameof(IsNodeExpanded)) + { + if (_childrenCalculated) + { + return; + } + + _childrenCalculated = true; + + // TODO: What cancellationtoken to use here? + CalculateChildrenAsync(CancellationToken.None) + .ContinueWith(task => + { + if (task.Exception is not null) + { + _childrenCalculated = false; + return; + } + + ChildItems.Clear(); + + foreach (var item in task.Result) + { + ChildItems.Add(item); + } + }, + + // Use the UI thread synchronization context for calling back to the UI + // thread to add the tiems + TaskScheduler.FromCurrentSynchronizationContext()); + + } + }; + } + + private async Task> CalculateChildrenAsync(CancellationToken cancellationToken) + { + var valueTrackedItems = await _valueTrackingService.TrackValueSourceAsync( + _solution, + TrackedItem, + cancellationToken).ConfigureAwait(false); + + // TODO: Use pooled item here? + var builder = new List(); + foreach (var valueTrackedItem in valueTrackedItems) + { + builder.Add(new ValueTrackingTreeItemViewModel( + valueTrackedItem, + _solution, + _valueTrackingService)); + } + + return builder.ToImmutableArray(); + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs new file mode 100644 index 0000000000000..486bb1ec71c90 --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class ValueTrackingTreeViewModel : INotifyPropertyChanged + { + public ValueTrackingTreeViewModel(ValueTrackingTreeItemViewModel root) + { + Roots.Add(root); + } + + public ObservableCollection Roots { get; } = new(); + + public event PropertyChangedEventHandler? PropertyChanged; + + private void SetProperty(ref T field, T value, [CallerMemberName] string name = "") + { + if (EqualityComparer.Default.Equals(field, value)) + { + return; + } + + field = value; + NotifyPropertyChanged(name); + } + + private void NotifyPropertyChanged(string name) + => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + } +} diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf index 6ab46a153fc53..2e5317d02b9e6 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf @@ -422,6 +422,21 @@ Přejít na implementaci + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Inicializovat Interactive přes projekt diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf index 39691247a87d9..4027a95cb4251 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf @@ -422,6 +422,21 @@ Zur Implementierung wechseln + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Interaktiv mit dem Projekt initialisieren diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf index 63941e8dbc88d..927958975d1ed 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf @@ -422,6 +422,21 @@ Ir a la implementación + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Inicializar el elemento interactivo con el proyecto diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf index 5eaa9a71bec58..a7b29c442ae2c 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf @@ -422,6 +422,21 @@ Accéder à l'implémentation + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Initialiser Interactive avec le projet diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf index bccc689169acb..d0f71b5ac9223 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf @@ -422,6 +422,21 @@ Vai all'implementazione + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Inizializza Interactive con il progetto diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf index 2ce24c0fc615d..ad582208aea51 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf @@ -422,6 +422,21 @@ 実装に移動 + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project プロジェクトでインタラクティブを初期化 diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf index fed9ec757ba77..bd4c80e62708c 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf @@ -422,6 +422,21 @@ 구현으로 이동 + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project 프로젝트에서 Interactive 초기화 diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf index bb907800c0146..6eeca8d5d5a35 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf @@ -422,6 +422,21 @@ Przejdź do implementacji + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Inicjuj środowisko interaktywne z projektem diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf index 5b6cd2c43c66c..fdc3219090701 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf @@ -422,6 +422,21 @@ Ir para Implementação + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Inicializar Interativo com o Projeto diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf index d68b6204e131e..ec8809be173ed 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf @@ -422,6 +422,21 @@ Перейти к реализации + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Инициализировать интерактивное окно с проектом diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf index 1d0204a6df606..4190512bc5068 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf @@ -422,6 +422,21 @@ Uygulamaya Git + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Projeyi Etkileşimli Pencerede Başlat diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf index 316d4be560b9c..984d36ecf7968 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf @@ -422,6 +422,21 @@ 转到实现 + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project 对交互窗口进行项目初始化 diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf index c01b09934ee99..223c86fd530dc 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf @@ -422,6 +422,21 @@ 前往實作 + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project 使用專案將 Interactive 初始化 From 43452422ecc14b644f446dc7203b05d7b27d04b9 Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Fri, 12 Mar 2021 11:14:10 -0800 Subject: [PATCH 002/127] Add unit tests --- .../ValueTracking/IValueTrackingService.cs | 0 .../Core}/ValueTracking/ValueTrackedItem.cs | 0 .../ValueTracking/ValueTrackingService.cs | 50 ++++++---- .../ValueTracking/CSharpValueTrackingTests.cs | 98 +++++++++++++++++++ .../Microsoft.CodeAnalysis.Features.csproj | 3 + .../DataFlowWindow/DataFlowCommandHandler.cs | 37 ------- .../DataFlowEditorCommandArgs.cs | 22 ----- .../Def/DataFlowWindow/DataFlowToolWindow.cs | 24 ----- 8 files changed, 134 insertions(+), 100 deletions(-) rename src/{Features/Core/Portable => EditorFeatures/Core}/ValueTracking/IValueTrackingService.cs (100%) rename src/{Features/Core/Portable => EditorFeatures/Core}/ValueTracking/ValueTrackedItem.cs (100%) rename src/{Features/Core/Portable => EditorFeatures/Core}/ValueTracking/ValueTrackingService.cs (74%) create mode 100644 src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs delete mode 100644 src/VisualStudio/Core/Def/DataFlowWindow/DataFlowCommandHandler.cs delete mode 100644 src/VisualStudio/Core/Def/DataFlowWindow/DataFlowEditorCommandArgs.cs delete mode 100644 src/VisualStudio/Core/Def/DataFlowWindow/DataFlowToolWindow.cs diff --git a/src/Features/Core/Portable/ValueTracking/IValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs similarity index 100% rename from src/Features/Core/Portable/ValueTracking/IValueTrackingService.cs rename to src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs diff --git a/src/Features/Core/Portable/ValueTracking/ValueTrackedItem.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs similarity index 100% rename from src/Features/Core/Portable/ValueTracking/ValueTrackedItem.cs rename to src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs diff --git a/src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs similarity index 74% rename from src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs rename to src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 581c63ada04a7..84334730010b1 100644 --- a/src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using System.Threading; @@ -35,19 +36,14 @@ public async Task> TrackValueSourceAsync( ISymbol symbol, CancellationToken cancellationToken) { - switch (symbol) + var referneceFinder = GetReferenceFinder(symbol); + if (referneceFinder is null) { - case IPropertySymbol: - case ILocalSymbol: - case IFieldSymbol: - { - var assignments = await TrackAssignmentsAsync(solution, symbol, cancellationToken).ConfigureAwait(false); - return assignments.Select(a => new ValueTrackedItem(a.Location.Location, symbol)).ToImmutableArray(); - } - - default: - return ImmutableArray.Empty; + return ImmutableArray.Empty; } + + var assignments = await TrackAssignmentsAsync(solution, symbol, referneceFinder, cancellationToken).ConfigureAwait(false); + return assignments.Select(a => new ValueTrackedItem(a.Location.Location, symbol)).ToImmutableArray(); } public async Task> TrackValueSourceAsync( @@ -60,7 +56,13 @@ public async Task> TrackValueSourceAsync( if (previousTrackedItem.PreviousTrackedItem is null) { var symbol = previousTrackedItem.Symbol; - var assignments = await TrackAssignmentsAsync(solution, symbol, cancellationToken).ConfigureAwait(false); + var referenceFinder = GetReferenceFinder(symbol); + if (referenceFinder is null) + { + return ImmutableArray.Empty; + } + + var assignments = await TrackAssignmentsAsync(solution, symbol, referenceFinder, cancellationToken).ConfigureAwait(false); return assignments.Select(a => new ValueTrackedItem(a.Location.Location, symbol, previousTrackedItem: previousTrackedItem)).ToImmutableArray(); } @@ -82,13 +84,14 @@ public async Task> TrackValueSourceAsync( private static async Task> TrackAssignmentsAsync( Solution solution, ISymbol symbol, + IReferenceFinder referenceFinder, CancellationToken cancellationToken) { using var _ = PooledObjects.ArrayBuilder.GetInstance(out var builder); - var projectsToSearch = await ReferenceFinders.Property.DetermineProjectsToSearchAsync(symbol, solution, solution.Projects.ToImmutableHashSet(), cancellationToken).ConfigureAwait(false); + var projectsToSearch = await referenceFinder.DetermineProjectsToSearchAsync(symbol, solution, solution.Projects.ToImmutableHashSet(), cancellationToken).ConfigureAwait(false); foreach (var project in projectsToSearch) { - var documentsToSearch = await ReferenceFinders.Property.DetermineDocumentsToSearchAsync( + var documentsToSearch = await referenceFinder.DetermineDocumentsToSearchAsync( symbol, project, project.Documents.ToImmutableHashSet(), @@ -99,7 +102,7 @@ private static async Task> TrackAssignmentsAsync( { var syntaxFacts = document.GetRequiredLanguageService(); var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var referencesInDocument = await ReferenceFinders.Property.FindReferencesInDocumentAsync( + var referencesInDocument = await referenceFinder.FindReferencesInDocumentAsync( symbol, document, semanticModel, @@ -121,9 +124,13 @@ private static async Task> TrackFromPropertyAss var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var symbolInfo = semanticModel.GetSymbolInfo(valueTrackedItem.ExpressionNode); - if (symbolInfo.Symbol is not null) + var referenceFinder = GetReferenceFinder(symbolInfo.Symbol); + + if (referenceFinder is not null) { - var assignments = await TrackAssignmentsAsync(solution, symbolInfo.Symbol, cancellationToken).ConfigureAwait(false); + RoslynDebug.AssertNotNull(symbolInfo.Symbol); + + var assignments = await TrackAssignmentsAsync(solution, symbolInfo.Symbol, referenceFinder, cancellationToken).ConfigureAwait(false); return assignments .Select(a => new ValueTrackedItem(a.Location.Location, symbolInfo.Symbol, previousTrackedItem: valueTrackedItem)) .ToImmutableArray(); @@ -131,5 +138,14 @@ private static async Task> TrackFromPropertyAss return ImmutableArray.Empty; } + + private static IReferenceFinder? GetReferenceFinder(ISymbol? symbol) + => symbol switch + { + IPropertySymbol => ReferenceFinders.Property, + IFieldSymbol => ReferenceFinders.Field, + ILocalSymbol => ReferenceFinders.Local, + _ => null + }; } } diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs new file mode 100644 index 0000000000000..e96fb50a4c581 --- /dev/null +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ValueTracking; +using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.Shared.Extensions; +using System.Threading; +using Microsoft.CodeAnalysis.Text; +using Xunit; +using Microsoft.CodeAnalysis.Test.Utilities; + +namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking +{ + [UseExportProvider] + public class CSharpValueTrackingTests + { + private static async Task> GetTrackedItemsAsync(TestWorkspace testWorkspace, CancellationToken cancellationToken = default) + { + var cursorDocument = testWorkspace.DocumentWithCursor; + var document = testWorkspace.CurrentSolution.GetRequiredDocument(cursorDocument.Id); + var syntaxTree = await document.GetRequiredSyntaxTreeAsync(cancellationToken); + var textSpan = new TextSpan(cursorDocument.CursorPosition!.Value, 0); + var location = Location.Create(syntaxTree, textSpan); + var symbol = await GetSelectedSymbolAsync(textSpan, document, cancellationToken); + var service = testWorkspace.Services.GetRequiredService(); + return await service.TrackValueSourceAsync(testWorkspace.CurrentSolution, location, symbol, cancellationToken); + + } + + private static async Task GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) + { + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var selectedNode = root.FindNode(textSpan); + + Assert.NotNull(selectedNode); + + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var selectedSymbol = + semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol + ?? semanticModel.GetDeclaredSymbol(selectedNode, cancellationToken); + + Assert.NotNull(selectedSymbol); + return selectedSymbol!; + } + + [Fact] + public async Task TestPropertyAsync() + { + var code = +@" +class C +{ + public string $$S { get; set; } = """"""; + + public void SetS(string s) + { + S = s; + } + + public string GetS() => S; +} +"; + using var workspace = TestWorkspace.CreateCSharp(code); + var initialItems = await GetTrackedItemsAsync(workspace); + + Assert.Equal(2, initialItems.Length); + } + + [Fact] + public async Task TestFieldAsync() + { + var code = +@" +class C +{ + private string $$_s = """"""; + + public void SetS(string s) + { + _s = s; + } + + public string GetS() => _s; +}"; + using var workspace = TestWorkspace.CreateCSharp(code); + var initialItems = await GetTrackedItemsAsync(workspace); + + Assert.Equal(2, initialItems.Length); + } + } +} diff --git a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj index bf59d330743a2..329ab439a18f7 100644 --- a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj +++ b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj @@ -119,6 +119,9 @@ + + + diff --git a/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowCommandHandler.cs b/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowCommandHandler.cs deleted file mode 100644 index cf53b8a500771..0000000000000 --- a/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowCommandHandler.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.ComponentModel.Composition; -using Microsoft.CodeAnalysis.Editor; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.VisualStudio.Commanding; -using Microsoft.VisualStudio.Utilities; - -namespace Microsoft.VisualStudio.LanguageServices.DataFlowWindow -{ - [Export(typeof(ICommandHandler))] - [ContentType(ContentTypeNames.RoslynContentType)] - [Name(PredefinedCommandHandlerNames.GoToDataFlow)] - internal class DataFlowCommandHandler : ICommandHandler - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public DataFlowCommandHandler() - { - } - - public string DisplayName => "Go to data flow"; - - public bool ExecuteCommand(DataFlowEditorCommandArgs args, CommandExecutionContext executionContext) - { - throw new NotImplementedException(); - } - - public CommandState GetCommandState(DataFlowEditorCommandArgs args) - { - throw new NotImplementedException(); - } - } -} diff --git a/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowEditorCommandArgs.cs b/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowEditorCommandArgs.cs deleted file mode 100644 index 64d0a85908b5b..0000000000000 --- a/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowEditorCommandArgs.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Text.Editor.Commanding; - -namespace Microsoft.VisualStudio.LanguageServices.DataFlowWindow -{ - internal class DataFlowEditorCommandArgs : EditorCommandArgs - { - public DataFlowEditorCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer) - { - } - } -} diff --git a/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowToolWindow.cs b/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowToolWindow.cs deleted file mode 100644 index 325c47176f9d1..0000000000000 --- a/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowToolWindow.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.InteropServices; -using System.Windows.Controls; -using Microsoft.VisualStudio.Shell; - -namespace Microsoft.VisualStudio.LanguageServices.DataFlowWindow -{ - [Guid("60a19d42-2dd7-43f3-be90-c7a9cb7d28f4")] - internal class DataFlowToolWindow : ToolWindowPane - { - public DataFlowToolWindow() - { - this.Caption = "Data Flow Tool Window"; - Content = new TextBlock() - { - Text = "Testing" - }; - } - } -} From 1727f28826dd92964bc86b4823e582db36df46ef Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Thu, 11 Mar 2021 15:56:27 -0800 Subject: [PATCH 003/127] Working UI and some functions --- ...oft.CodeAnalysis.EditorFeatures.Wpf.csproj | 1 + .../Commands/PredefinedCommandHandlerNames.cs | 5 + .../ValueTracking/IValueTrackingService.cs | 17 ++ .../ValueTracking/ValueTrackedItem.cs | 26 +++ .../ValueTracking/ValueTrackingService.cs | 135 ++++++++++++++ src/VisualStudio/Core/Def/Commands.vsct | 19 ++ .../DataFlowWindow/DataFlowCommandHandler.cs | 37 ++++ .../DataFlowEditorCommandArgs.cs | 22 +++ .../Def/DataFlowWindow/DataFlowToolWindow.cs | 24 +++ src/VisualStudio/Core/Def/Guids.cs | 3 + .../Core/Def/ID.RoslynCommands.cs | 1 + .../Def/Implementation/CommandBindings.cs | 5 + ...osoft.VisualStudio.LanguageServices.csproj | 5 +- .../Core/Def/PackageRegistration.pkgdef | 6 + src/VisualStudio/Core/Def/RoslynPackage.cs | 21 ++- .../ValueTrackingCommandHandler.cs | 174 ++++++++++++++++++ .../ValueTrackingEditorCommandArgs.cs | 22 +++ .../ValueTracking/ValueTrackingToolWindow.cs | 58 ++++++ .../Def/ValueTracking/ValueTrackingTree.xaml | 72 ++++++++ .../ValueTracking/ValueTrackingTree.xaml.cs | 35 ++++ .../ValueTrackingTreeItemViewModel.cs | 163 ++++++++++++++++ .../ValueTrackingTreeViewModel.cs | 37 ++++ .../Core/Def/xlf/Commands.vsct.cs.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.de.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.es.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.fr.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.it.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.ja.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.ko.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.pl.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.pt-BR.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.ru.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.tr.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.zh-Hans.xlf | 15 ++ .../Core/Def/xlf/Commands.vsct.zh-Hant.xlf | 15 ++ 35 files changed, 1075 insertions(+), 8 deletions(-) create mode 100644 src/Features/Core/Portable/ValueTracking/IValueTrackingService.cs create mode 100644 src/Features/Core/Portable/ValueTracking/ValueTrackedItem.cs create mode 100644 src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs create mode 100644 src/VisualStudio/Core/Def/DataFlowWindow/DataFlowCommandHandler.cs create mode 100644 src/VisualStudio/Core/Def/DataFlowWindow/DataFlowEditorCommandArgs.cs create mode 100644 src/VisualStudio/Core/Def/DataFlowWindow/DataFlowToolWindow.cs create mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs create mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingEditorCommandArgs.cs create mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs create mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml create mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs create mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs create mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs diff --git a/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj b/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj index 4211ba99baff2..19a867ba77c57 100644 --- a/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj +++ b/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj @@ -69,6 +69,7 @@ + diff --git a/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.cs b/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.cs index 545e53524b7d7..531132d451939 100644 --- a/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.cs +++ b/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.cs @@ -178,5 +178,10 @@ internal static class PredefinedCommandHandlerNames /// Command handler name for Edit and Continue file save handler. /// public const string EditAndContinueFileSave = "Edit and Continue Save File Handler"; + + /// + /// Command handler name for showing the Value Tracking tool window. + /// + public const string ShowValueTracking = "Show Value Tracking"; } } diff --git a/src/Features/Core/Portable/ValueTracking/IValueTrackingService.cs b/src/Features/Core/Portable/ValueTracking/IValueTrackingService.cs new file mode 100644 index 0000000000000..8504d153808f9 --- /dev/null +++ b/src/Features/Core/Portable/ValueTracking/IValueTrackingService.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; + +namespace Microsoft.CodeAnalysis.ValueTracking +{ + internal interface IValueTrackingService : IWorkspaceService + { + Task> TrackValueSourceAsync(Solution solution, Location location, ISymbol symbol, CancellationToken cancellationToken); + Task> TrackValueSourceAsync(Solution solution, ValueTrackedItem previousTrackedItem, CancellationToken cancellationToken); + } +} diff --git a/src/Features/Core/Portable/ValueTracking/ValueTrackedItem.cs b/src/Features/Core/Portable/ValueTracking/ValueTrackedItem.cs new file mode 100644 index 0000000000000..46247c807d43f --- /dev/null +++ b/src/Features/Core/Portable/ValueTracking/ValueTrackedItem.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.ValueTracking +{ + internal class ValueTrackedItem + { + public Location Location { get; } + public ISymbol Symbol { get; } + public SyntaxNode? ExpressionNode { get; } + public ValueTrackedItem? PreviousTrackedItem { get; } + + public ValueTrackedItem( + Location location, + ISymbol symbol, + SyntaxNode? expressionNode = null, + ValueTrackedItem? previousTrackedItem = null) + { + Location = location; + Symbol = symbol; + ExpressionNode = expressionNode; + PreviousTrackedItem = previousTrackedItem; + } + } +} diff --git a/src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs b/src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs new file mode 100644 index 0000000000000..581c63ada04a7 --- /dev/null +++ b/src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs @@ -0,0 +1,135 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.FindSymbols.Finders; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServices; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ValueTracking +{ + [ExportWorkspaceService(typeof(IValueTrackingService)), Shared] + internal class ValueTrackingService : IValueTrackingService + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public ValueTrackingService() + { + } + + public async Task> TrackValueSourceAsync( + Solution solution, + Location location, + ISymbol symbol, + CancellationToken cancellationToken) + { + switch (symbol) + { + case IPropertySymbol: + case ILocalSymbol: + case IFieldSymbol: + { + var assignments = await TrackAssignmentsAsync(solution, symbol, cancellationToken).ConfigureAwait(false); + return assignments.Select(a => new ValueTrackedItem(a.Location.Location, symbol)).ToImmutableArray(); + } + + default: + return ImmutableArray.Empty; + } + } + + public async Task> TrackValueSourceAsync( + Solution solution, + ValueTrackedItem previousTrackedItem, + CancellationToken cancellationToken) + { + RoslynDebug.AssertNotNull(previousTrackedItem.Location.SourceTree); + + if (previousTrackedItem.PreviousTrackedItem is null) + { + var symbol = previousTrackedItem.Symbol; + var assignments = await TrackAssignmentsAsync(solution, symbol, cancellationToken).ConfigureAwait(false); + return assignments.Select(a => new ValueTrackedItem(a.Location.Location, symbol, previousTrackedItem: previousTrackedItem)).ToImmutableArray(); + } + + // There's no interesting node + if (previousTrackedItem.ExpressionNode is null) + { + return ImmutableArray.Empty; + } + + if (previousTrackedItem.Symbol is IPropertySymbol) + { + var document = solution.GetRequiredDocument(previousTrackedItem.Location.SourceTree); + return await TrackFromPropertyAssignmentAsync(solution, document, previousTrackedItem, cancellationToken).ConfigureAwait(false); + } + + throw new Exception(); + } + + private static async Task> TrackAssignmentsAsync( + Solution solution, + ISymbol symbol, + CancellationToken cancellationToken) + { + using var _ = PooledObjects.ArrayBuilder.GetInstance(out var builder); + var projectsToSearch = await ReferenceFinders.Property.DetermineProjectsToSearchAsync(symbol, solution, solution.Projects.ToImmutableHashSet(), cancellationToken).ConfigureAwait(false); + foreach (var project in projectsToSearch) + { + var documentsToSearch = await ReferenceFinders.Property.DetermineDocumentsToSearchAsync( + symbol, + project, + project.Documents.ToImmutableHashSet(), + FindReferencesSearchOptions.Default, + cancellationToken).ConfigureAwait(false); + + foreach (var document in documentsToSearch) + { + var syntaxFacts = document.GetRequiredLanguageService(); + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var referencesInDocument = await ReferenceFinders.Property.FindReferencesInDocumentAsync( + symbol, + document, + semanticModel, + FindReferencesSearchOptions.Default, + cancellationToken).ConfigureAwait(false); + + builder.AddRange(referencesInDocument + .Where(r => r.Location.IsWrittenTo)); + } + } + + return builder.AsImmutableOrEmpty(); + } + + private static async Task> TrackFromPropertyAssignmentAsync(Solution solution, Document document, ValueTrackedItem valueTrackedItem, CancellationToken cancellationToken) + { + RoslynDebug.AssertNotNull(valueTrackedItem.ExpressionNode); + + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var symbolInfo = semanticModel.GetSymbolInfo(valueTrackedItem.ExpressionNode); + + if (symbolInfo.Symbol is not null) + { + var assignments = await TrackAssignmentsAsync(solution, symbolInfo.Symbol, cancellationToken).ConfigureAwait(false); + return assignments + .Select(a => new ValueTrackedItem(a.Location.Location, symbolInfo.Symbol, previousTrackedItem: valueTrackedItem)) + .ToImmutableArray(); + } + + return ImmutableArray.Empty; + } + } +} diff --git a/src/VisualStudio/Core/Def/Commands.vsct b/src/VisualStudio/Core/Def/Commands.vsct index 422b93c7d0bfd..591fab85c9ba0 100644 --- a/src/VisualStudio/Core/Def/Commands.vsct +++ b/src/VisualStudio/Core/Def/Commands.vsct @@ -411,6 +411,19 @@ RemoveUnusedReferences + + @@ -516,6 +529,10 @@ + + + + @@ -601,6 +618,8 @@ + + diff --git a/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowCommandHandler.cs b/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowCommandHandler.cs new file mode 100644 index 0000000000000..cf53b8a500771 --- /dev/null +++ b/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowCommandHandler.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.ComponentModel.Composition; +using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.VisualStudio.Commanding; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.VisualStudio.LanguageServices.DataFlowWindow +{ + [Export(typeof(ICommandHandler))] + [ContentType(ContentTypeNames.RoslynContentType)] + [Name(PredefinedCommandHandlerNames.GoToDataFlow)] + internal class DataFlowCommandHandler : ICommandHandler + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public DataFlowCommandHandler() + { + } + + public string DisplayName => "Go to data flow"; + + public bool ExecuteCommand(DataFlowEditorCommandArgs args, CommandExecutionContext executionContext) + { + throw new NotImplementedException(); + } + + public CommandState GetCommandState(DataFlowEditorCommandArgs args) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowEditorCommandArgs.cs b/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowEditorCommandArgs.cs new file mode 100644 index 0000000000000..64d0a85908b5b --- /dev/null +++ b/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowEditorCommandArgs.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Editor.Commanding; + +namespace Microsoft.VisualStudio.LanguageServices.DataFlowWindow +{ + internal class DataFlowEditorCommandArgs : EditorCommandArgs + { + public DataFlowEditorCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer) + { + } + } +} diff --git a/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowToolWindow.cs b/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowToolWindow.cs new file mode 100644 index 0000000000000..325c47176f9d1 --- /dev/null +++ b/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowToolWindow.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; +using System.Windows.Controls; +using Microsoft.VisualStudio.Shell; + +namespace Microsoft.VisualStudio.LanguageServices.DataFlowWindow +{ + [Guid("60a19d42-2dd7-43f3-be90-c7a9cb7d28f4")] + internal class DataFlowToolWindow : ToolWindowPane + { + public DataFlowToolWindow() + { + this.Caption = "Data Flow Tool Window"; + Content = new TextBlock() + { + Text = "Testing" + }; + } + } +} diff --git a/src/VisualStudio/Core/Def/Guids.cs b/src/VisualStudio/Core/Def/Guids.cs index 6f058e3c50002..994acb0fbbebc 100644 --- a/src/VisualStudio/Core/Def/Guids.cs +++ b/src/VisualStudio/Core/Def/Guids.cs @@ -125,6 +125,9 @@ internal static class Guids public static readonly Guid RoslynCommandSetId = new(RoslynCommandSetIdString); public static readonly Guid RoslynGroupId = new(RoslynGroupIdString); + public const string ValueTrackingToolWindowIdString = "60a19d42-2dd7-43f3-be90-c7a9cb7d28f4"; + public static readonly Guid ValueTrackingToolWindowId = new(ValueTrackingToolWindowIdString); + // TODO: Remove pending https://github.com/dotnet/roslyn/issues/8927 . // Interactive guids public const string InteractiveCommandSetIdString = "00B8868B-F9F5-4970-A048-410B05508506"; diff --git a/src/VisualStudio/Core/Def/ID.RoslynCommands.cs b/src/VisualStudio/Core/Def/ID.RoslynCommands.cs index d89285327286e..ad65fd8e87b1f 100644 --- a/src/VisualStudio/Core/Def/ID.RoslynCommands.cs +++ b/src/VisualStudio/Core/Def/ID.RoslynCommands.cs @@ -50,6 +50,7 @@ public static class RoslynCommands public const int RunCodeAnalysisForProject = 0x0201; public const int RemoveUnusedReferences = 0x0202; + public const int GoToValueTrackingWindow = 0x0203; } } } diff --git a/src/VisualStudio/Core/Def/Implementation/CommandBindings.cs b/src/VisualStudio/Core/Def/Implementation/CommandBindings.cs index 27a6efccbd0ea..b2992efa119ab 100644 --- a/src/VisualStudio/Core/Def/Implementation/CommandBindings.cs +++ b/src/VisualStudio/Core/Def/Implementation/CommandBindings.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.Editor.Commanding.Commands; using Microsoft.VisualStudio.Editor.Commanding; using Microsoft.VisualStudio.LanguageServices; +using Microsoft.VisualStudio.LanguageServices.ValueTracking; namespace Microsoft.VisualStudio.Editor.Implementation { @@ -28,5 +29,9 @@ internal sealed class CommandBindings [Export] [CommandBinding(Guids.CSharpGroupIdString, ID.CSharpCommands.ContextOrganizeRemoveAndSort, typeof(SortAndRemoveUnnecessaryImportsCommandArgs))] internal CommandBindingDefinition contextOrganizeRemoveAndSortCommandBinding; + + [Export] + [CommandBinding(Guids.RoslynGroupIdString, ID.RoslynCommands.GoToValueTrackingWindow, typeof(ValueTrackingEditorCommandArgs))] + internal CommandBindingDefinition gotoDataFlowToolCommandBinding; } } diff --git a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj index 5fab552e80282..1eb93e319b038 100644 --- a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj +++ b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj @@ -33,7 +33,7 @@ 6cf2e545-6109-4730-8883-cf43d7aec3e1 - + @@ -199,8 +199,7 @@ - + diff --git a/src/VisualStudio/Core/Def/PackageRegistration.pkgdef b/src/VisualStudio/Core/Def/PackageRegistration.pkgdef index 27e41b380e9ba..d1b8281f52747 100644 --- a/src/VisualStudio/Core/Def/PackageRegistration.pkgdef +++ b/src/VisualStudio/Core/Def/PackageRegistration.pkgdef @@ -24,3 +24,9 @@ "Value"=dword:00000000 "Title"="Enable experimental C#/VB LSP completion experience" "PreviewPaneChannels"="IntPreview,int.main" + +// The option page configuration is duplicated in RoslynPackage +// [ProvideToolWindow(typeof(ValueTracking.ValueTrackingToolWindow))] +[$RootKey$\ToolWindows\{60a19d42-2dd7-43f3-be90-c7a9cb7d28f4}] +"Name"="Microsoft.VisualStudio.LanguageServices.ValueTracking.ValueTrackingToolWindow" +@="{6cf2e545-6109-4730-8883-cf43d7aec3e1}" diff --git a/src/VisualStudio/Core/Def/RoslynPackage.cs b/src/VisualStudio/Core/Def/RoslynPackage.cs index 7261ce5c45d59..57e373566a571 100644 --- a/src/VisualStudio/Core/Def/RoslynPackage.cs +++ b/src/VisualStudio/Core/Def/RoslynPackage.cs @@ -19,28 +19,23 @@ using Microsoft.CodeAnalysis.Logging; using Microsoft.CodeAnalysis.Notification; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.CodeAnalysis.Versions; using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.LanguageServices.ColorSchemes; using Microsoft.VisualStudio.LanguageServices.Experimentation; using Microsoft.VisualStudio.LanguageServices.Implementation; -using Microsoft.VisualStudio.LanguageServices.Implementation.DesignerAttribute; using Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics; using Microsoft.VisualStudio.LanguageServices.Implementation.Interactive; using Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.RuleSets; -using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectTelemetry; using Microsoft.VisualStudio.LanguageServices.Implementation.TableDataSource; -using Microsoft.VisualStudio.LanguageServices.Implementation.TodoComments; using Microsoft.VisualStudio.LanguageServices.Implementation.UnusedReferences; using Microsoft.VisualStudio.LanguageServices.Telemetry; using Microsoft.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -using Microsoft.VisualStudio.Shell.ServiceBroker; using Microsoft.VisualStudio.TaskStatusCenter; using Microsoft.VisualStudio.Telemetry; using Microsoft.VisualStudio.TextManager.Interop; @@ -51,6 +46,9 @@ namespace Microsoft.VisualStudio.LanguageServices.Setup { [Guid(Guids.RoslynPackageIdString)] + + // The option page configuration is duplicated in PackageRegistration.pkgdef + [ProvideToolWindow(typeof(ValueTracking.ValueTrackingToolWindow))] internal sealed class RoslynPackage : AbstractPackage { // The randomly-generated key name is used for serializing the ILSpy decompiler EULA preference to the .SUO @@ -188,6 +186,19 @@ protected override async Task LoadComponentsAsync(CancellationToken cancellation LoadComponentsBackgroundAsync(cancellationToken).Forget(); } + // Overrides for VSSDK003 fix + // See https://github.com/Microsoft/VSSDK-Analyzers/blob/main/doc/VSSDK003.md + public override IVsAsyncToolWindowFactory GetAsyncToolWindowFactory(Guid toolWindowType) + => toolWindowType == typeof(ValueTracking.ValueTrackingToolWindow).GUID + ? this + : base.GetAsyncToolWindowFactory(toolWindowType); + + protected override string GetToolWindowTitle(Type toolWindowType, int id) + => base.GetToolWindowTitle(toolWindowType, id); + + protected override Task InitializeToolWindowAsync(Type toolWindowType, int id, CancellationToken cancellationToken) + => Task.FromResult((object?)null); + private async Task LoadComponentsBackgroundAsync(CancellationToken cancellationToken) { await TaskScheduler.Default; diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs new file mode 100644 index 0000000000000..2bcbb7f1463eb --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs @@ -0,0 +1,174 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.ComponentModel.Composition; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.Editor.Shared.Extensions; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServices; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.ValueTracking; +using Microsoft.VisualStudio.Commanding; +using Microsoft.VisualStudio.GraphModel.CodeSchema; +using Microsoft.VisualStudio.LanguageServices.Setup; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Utilities; +using IAsyncServiceProvider = Microsoft.VisualStudio.Shell.IAsyncServiceProvider; +using Task = System.Threading.Tasks.Task; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + [Export(typeof(ICommandHandler))] + [ContentType(ContentTypeNames.RoslynContentType)] + [Name(PredefinedCommandHandlerNames.ShowValueTracking)] + internal class ValueTrackingCommandHandler : ICommandHandler + { + private readonly IAsyncServiceProvider _serviceProvider; + private readonly IThreadingContext _threadingContext; + + private RoslynPackage? _roslynPackage; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public ValueTrackingCommandHandler(SVsServiceProvider serviceProvider, IThreadingContext threadingContext) + { + _serviceProvider = (IAsyncServiceProvider)serviceProvider; + _threadingContext = threadingContext; + } + + public string DisplayName => "Go to value tracking"; + + public CommandState GetCommandState(ValueTrackingEditorCommandArgs args) + => CommandState.Available; + + public bool ExecuteCommand(ValueTrackingEditorCommandArgs args, CommandExecutionContext executionContext) + { + var cancellationToken = executionContext.OperationContext.UserCancellationToken; + var caretPosition = args.TextView.GetCaretPoint(args.SubjectBuffer); + if (!caretPosition.HasValue) + { + return false; + } + + var textSpan = new TextSpan(caretPosition.Value.Position, 0); + var sourceTextContainer = args.SubjectBuffer.AsTextContainer(); + var document = sourceTextContainer.GetOpenDocumentInCurrentContext(); + if (document is null) + { + return false; + } + + var selectedSymbol = _threadingContext.JoinableTaskFactory.Run(() => GetSelectedSymbolAsync(textSpan, document, cancellationToken)); + if (selectedSymbol is null) + { + return false; + } + + var syntaxTree = document.GetRequiredSyntaxTreeSynchronously(cancellationToken); + var location = Location.Create(syntaxTree, textSpan); + + _threadingContext.JoinableTaskFactory.Run(() => ShowToolWindowAsync(selectedSymbol, location, document.Project.Solution, cancellationToken)); + + return true; + } + + private static async Task GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) + { + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var selectedNode = root.FindNode(textSpan); + if (selectedNode is null) + { + return null; + } + + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var selectedSymbol = + semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol + ?? semanticModel.GetDeclaredSymbol(selectedNode, cancellationToken); + + if (selectedSymbol is null) + { + return null; + } + + return selectedSymbol switch + { + ILocalSymbol + or IPropertySymbol { SetMethod: not null } + or IFieldSymbol { IsReadOnly: false } + or IEventSymbol + or IParameterSymbol + => selectedSymbol, + + _ => null + }; + } + + private async Task ShowToolWindowAsync(ISymbol selectedSymbol, Location location, Solution solution, CancellationToken cancellationToken) + { + var roslynPackage = await TryGetRoslynPackageAsync(cancellationToken).ConfigureAwait(false); + + if (roslynPackage is null) + { + return; + } + + var dataFlowItem = new ValueTrackingTreeItemViewModel( + new ValueTrackedItem(location, selectedSymbol), + solution, + solution.Workspace.Services.GetRequiredService()); + + if (ValueTrackingToolWindow.Instance is null) + { + var factory = roslynPackage.GetAsyncToolWindowFactory(Guids.ValueTrackingToolWindowId); + + factory.CreateToolWindow(Guids.ValueTrackingToolWindowId, 0, dataFlowItem); + await factory.InitializeToolWindowAsync(Guids.ValueTrackingToolWindowId, 0); + + ValueTrackingToolWindow.Instance = (ValueTrackingToolWindow)await roslynPackage.ShowToolWindowAsync( + typeof(ValueTrackingToolWindow), + 0, + true, + roslynPackage.DisposalToken).ConfigureAwait(false); + } + else + { + ValueTrackingToolWindow.Instance.Root = dataFlowItem; + await roslynPackage.ShowToolWindowAsync( + typeof(ValueTrackingToolWindow), + 0, + false, + roslynPackage.DisposalToken).ConfigureAwait(false); + } + } + + private async ValueTask TryGetRoslynPackageAsync(CancellationToken cancellationToken) + { + if (_roslynPackage is null) + { + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var shell = (IVsShell7?)await _serviceProvider.GetServiceAsync(typeof(SVsShell)).ConfigureAwait(true); + Assumes.Present(shell); + await shell.LoadPackageAsync(typeof(RoslynPackage).GUID); + + if (ErrorHandler.Succeeded(((IVsShell)shell).IsPackageLoaded(typeof(RoslynPackage).GUID, out var package))) + { + _roslynPackage = (RoslynPackage)package; + } + } + + return _roslynPackage; + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingEditorCommandArgs.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingEditorCommandArgs.cs new file mode 100644 index 0000000000000..75e7930090a3a --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingEditorCommandArgs.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Editor.Commanding; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class ValueTrackingEditorCommandArgs : EditorCommandArgs + { + public ValueTrackingEditorCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer) + { + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs new file mode 100644 index 0000000000000..67c24e9cf3d5e --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Controls; +using Microsoft.CodeAnalysis; +using Microsoft.VisualStudio.Shell; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + [Guid(Guids.ValueTrackingToolWindowIdString)] + internal class ValueTrackingToolWindow : ToolWindowPane + { + public static ValueTrackingToolWindow? Instance { get; set; } + private readonly ValueTrackingTreeViewModel _viewModel; + + // Needed for VSSDK003 + // See https://github.com/Microsoft/VSSDK-Analyzers/blob/main/doc/VSSDK003.md + public ValueTrackingToolWindow(object o) + : base(null) + { + if (Instance is not null) + { + throw new Exception("Cannot initialize the window more than once"); + } + + this.Caption = "Value Tracking"; + + if (o is ValueTrackingTreeItemViewModel root) + { + _viewModel = new ValueTrackingTreeViewModel(root); + Content = new ValueTrackingTree(_viewModel); + } + else + { + throw new Exception("This shouldn't happen"); + } + } + + public ValueTrackingTreeItemViewModel Root + { + get => _viewModel.Roots.Single(); + set + { + _viewModel.Roots.Clear(); + _viewModel.Roots.Add(value); + } + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml new file mode 100644 index 0000000000000..2b19c1bce37bd --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs new file mode 100644 index 0000000000000..87ee81bb63c8b --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + /// + /// Interaction logic for ValueTrackingTree.xaml + /// + internal partial class ValueTrackingTree : UserControl + { + private readonly ValueTrackingTreeViewModel _viewModel; + + public ValueTrackingTree(ValueTrackingTreeViewModel viewModel) + { + DataContext = _viewModel = viewModel; + InitializeComponent(); + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs new file mode 100644 index 0000000000000..a82eb4936d020 --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs @@ -0,0 +1,163 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.ValueTracking; +using Roslyn.Utilities; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class TreeViewItemBase : INotifyPropertyChanged + { + private bool _isExpanded = false; + public bool IsNodeExpanded + { + get => _isExpanded; + set => SetProperty(ref _isExpanded, value); + } + + private bool _isSelected = false; + public bool IsNodeSelected + { + get => _isSelected; + set => SetProperty(ref _isSelected, value); + } + + public event PropertyChangedEventHandler? PropertyChanged; + + protected void SetProperty(ref T field, T value, [CallerMemberName] string name = "") + { + if (EqualityComparer.Default.Equals(field, value)) + { + return; + } + + field = value; + NotifyPropertyChanged(name); + } + + protected void NotifyPropertyChanged(string name) + => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + } + internal class EmptyTreeViewItem : TreeViewItemBase + { + public static EmptyTreeViewItem Instance { get; } = new(); + + private EmptyTreeViewItem() + { + } + } + + internal class ValueTrackingTreeItemViewModel : TreeViewItemBase + { + private bool _childrenCalculated; + private readonly Solution _solution; + private readonly IValueTrackingService _valueTrackingService; + + public ObservableCollection ChildItems { get; } = new(); + + public Location Location => TrackedItem.Location; + public ISymbol Symbol => TrackedItem.Symbol; + + public string FileName => Path.GetFileName(Location.SourceTree!.FilePath); + public string LineDisplay + { + get + { + var linePosition = Location.GetLineSpan().StartLinePosition; + return linePosition.Line.ToString(); + } + } + + public string SymbolName => Symbol.ToDisplayString(); + public string ContainerName => Symbol.ContainingSymbol.ToDisplayString(); + + public ValueTrackedItem TrackedItem { get; } + + public ValueTrackingTreeItemViewModel( + ValueTrackedItem trackedItem, + Solution solution, + IValueTrackingService valueTrackingService) + { + Contract.ThrowIfFalse(trackedItem.Location.IsInSource); + + ChildItems.Add(EmptyTreeViewItem.Instance); + + TrackedItem = trackedItem; + _solution = solution; + _valueTrackingService = valueTrackingService; + + ChildItems.CollectionChanged += (s, a) => + { + NotifyPropertyChanged(nameof(ChildItems)); + }; + + PropertyChanged += (s, a) => + { + if (a.PropertyName == nameof(IsNodeExpanded)) + { + if (_childrenCalculated) + { + return; + } + + _childrenCalculated = true; + + // TODO: What cancellationtoken to use here? + CalculateChildrenAsync(CancellationToken.None) + .ContinueWith(task => + { + if (task.Exception is not null) + { + _childrenCalculated = false; + return; + } + + ChildItems.Clear(); + + foreach (var item in task.Result) + { + ChildItems.Add(item); + } + }, + + // Use the UI thread synchronization context for calling back to the UI + // thread to add the tiems + TaskScheduler.FromCurrentSynchronizationContext()); + + } + }; + } + + private async Task> CalculateChildrenAsync(CancellationToken cancellationToken) + { + var valueTrackedItems = await _valueTrackingService.TrackValueSourceAsync( + _solution, + TrackedItem, + cancellationToken).ConfigureAwait(false); + + // TODO: Use pooled item here? + var builder = new List(); + foreach (var valueTrackedItem in valueTrackedItems) + { + builder.Add(new ValueTrackingTreeItemViewModel( + valueTrackedItem, + _solution, + _valueTrackingService)); + } + + return builder.ToImmutableArray(); + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs new file mode 100644 index 0000000000000..486bb1ec71c90 --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class ValueTrackingTreeViewModel : INotifyPropertyChanged + { + public ValueTrackingTreeViewModel(ValueTrackingTreeItemViewModel root) + { + Roots.Add(root); + } + + public ObservableCollection Roots { get; } = new(); + + public event PropertyChangedEventHandler? PropertyChanged; + + private void SetProperty(ref T field, T value, [CallerMemberName] string name = "") + { + if (EqualityComparer.Default.Equals(field, value)) + { + return; + } + + field = value; + NotifyPropertyChanged(name); + } + + private void NotifyPropertyChanged(string name) + => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + } +} diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf index 6ab46a153fc53..2e5317d02b9e6 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf @@ -422,6 +422,21 @@ Přejít na implementaci + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Inicializovat Interactive přes projekt diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf index 39691247a87d9..4027a95cb4251 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf @@ -422,6 +422,21 @@ Zur Implementierung wechseln + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Interaktiv mit dem Projekt initialisieren diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf index 63941e8dbc88d..927958975d1ed 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf @@ -422,6 +422,21 @@ Ir a la implementación + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Inicializar el elemento interactivo con el proyecto diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf index 5eaa9a71bec58..a7b29c442ae2c 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf @@ -422,6 +422,21 @@ Accéder à l'implémentation + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Initialiser Interactive avec le projet diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf index bccc689169acb..d0f71b5ac9223 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf @@ -422,6 +422,21 @@ Vai all'implementazione + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Inizializza Interactive con il progetto diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf index 2ce24c0fc615d..ad582208aea51 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf @@ -422,6 +422,21 @@ 実装に移動 + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project プロジェクトでインタラクティブを初期化 diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf index fed9ec757ba77..bd4c80e62708c 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf @@ -422,6 +422,21 @@ 구현으로 이동 + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project 프로젝트에서 Interactive 초기화 diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf index bb907800c0146..6eeca8d5d5a35 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf @@ -422,6 +422,21 @@ Przejdź do implementacji + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Inicjuj środowisko interaktywne z projektem diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf index 5b6cd2c43c66c..fdc3219090701 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf @@ -422,6 +422,21 @@ Ir para Implementação + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Inicializar Interativo com o Projeto diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf index d68b6204e131e..ec8809be173ed 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf @@ -422,6 +422,21 @@ Перейти к реализации + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Инициализировать интерактивное окно с проектом diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf index 1d0204a6df606..4190512bc5068 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf @@ -422,6 +422,21 @@ Uygulamaya Git + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project Projeyi Etkileşimli Pencerede Başlat diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf index 316d4be560b9c..984d36ecf7968 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf @@ -422,6 +422,21 @@ 转到实现 + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project 对交互窗口进行项目初始化 diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf index c01b09934ee99..223c86fd530dc 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf @@ -422,6 +422,21 @@ 前往實作 + + Show Value Tracking + Show Value Tracking + + + + ShowValueTrackingCommandName + ShowValueTrackingCommandName + + + + ViewEditorConfigSettings + ViewEditorConfigSettings + + Initialize Interactive with Project 使用專案將 Interactive 初始化 From a9b6e0734abf02cf1533fce2c966ccf61d18873e Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Fri, 12 Mar 2021 11:14:10 -0800 Subject: [PATCH 004/127] Add unit tests --- .../ValueTracking/IValueTrackingService.cs | 0 .../Core}/ValueTracking/ValueTrackedItem.cs | 0 .../ValueTracking/ValueTrackingService.cs | 50 ++++++---- .../ValueTracking/CSharpValueTrackingTests.cs | 98 +++++++++++++++++++ .../Microsoft.CodeAnalysis.Features.csproj | 3 + .../DataFlowWindow/DataFlowCommandHandler.cs | 37 ------- .../DataFlowEditorCommandArgs.cs | 22 ----- .../Def/DataFlowWindow/DataFlowToolWindow.cs | 24 ----- 8 files changed, 134 insertions(+), 100 deletions(-) rename src/{Features/Core/Portable => EditorFeatures/Core}/ValueTracking/IValueTrackingService.cs (100%) rename src/{Features/Core/Portable => EditorFeatures/Core}/ValueTracking/ValueTrackedItem.cs (100%) rename src/{Features/Core/Portable => EditorFeatures/Core}/ValueTracking/ValueTrackingService.cs (74%) create mode 100644 src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs delete mode 100644 src/VisualStudio/Core/Def/DataFlowWindow/DataFlowCommandHandler.cs delete mode 100644 src/VisualStudio/Core/Def/DataFlowWindow/DataFlowEditorCommandArgs.cs delete mode 100644 src/VisualStudio/Core/Def/DataFlowWindow/DataFlowToolWindow.cs diff --git a/src/Features/Core/Portable/ValueTracking/IValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs similarity index 100% rename from src/Features/Core/Portable/ValueTracking/IValueTrackingService.cs rename to src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs diff --git a/src/Features/Core/Portable/ValueTracking/ValueTrackedItem.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs similarity index 100% rename from src/Features/Core/Portable/ValueTracking/ValueTrackedItem.cs rename to src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs diff --git a/src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs similarity index 74% rename from src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs rename to src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 581c63ada04a7..84334730010b1 100644 --- a/src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using System.Threading; @@ -35,19 +36,14 @@ public async Task> TrackValueSourceAsync( ISymbol symbol, CancellationToken cancellationToken) { - switch (symbol) + var referneceFinder = GetReferenceFinder(symbol); + if (referneceFinder is null) { - case IPropertySymbol: - case ILocalSymbol: - case IFieldSymbol: - { - var assignments = await TrackAssignmentsAsync(solution, symbol, cancellationToken).ConfigureAwait(false); - return assignments.Select(a => new ValueTrackedItem(a.Location.Location, symbol)).ToImmutableArray(); - } - - default: - return ImmutableArray.Empty; + return ImmutableArray.Empty; } + + var assignments = await TrackAssignmentsAsync(solution, symbol, referneceFinder, cancellationToken).ConfigureAwait(false); + return assignments.Select(a => new ValueTrackedItem(a.Location.Location, symbol)).ToImmutableArray(); } public async Task> TrackValueSourceAsync( @@ -60,7 +56,13 @@ public async Task> TrackValueSourceAsync( if (previousTrackedItem.PreviousTrackedItem is null) { var symbol = previousTrackedItem.Symbol; - var assignments = await TrackAssignmentsAsync(solution, symbol, cancellationToken).ConfigureAwait(false); + var referenceFinder = GetReferenceFinder(symbol); + if (referenceFinder is null) + { + return ImmutableArray.Empty; + } + + var assignments = await TrackAssignmentsAsync(solution, symbol, referenceFinder, cancellationToken).ConfigureAwait(false); return assignments.Select(a => new ValueTrackedItem(a.Location.Location, symbol, previousTrackedItem: previousTrackedItem)).ToImmutableArray(); } @@ -82,13 +84,14 @@ public async Task> TrackValueSourceAsync( private static async Task> TrackAssignmentsAsync( Solution solution, ISymbol symbol, + IReferenceFinder referenceFinder, CancellationToken cancellationToken) { using var _ = PooledObjects.ArrayBuilder.GetInstance(out var builder); - var projectsToSearch = await ReferenceFinders.Property.DetermineProjectsToSearchAsync(symbol, solution, solution.Projects.ToImmutableHashSet(), cancellationToken).ConfigureAwait(false); + var projectsToSearch = await referenceFinder.DetermineProjectsToSearchAsync(symbol, solution, solution.Projects.ToImmutableHashSet(), cancellationToken).ConfigureAwait(false); foreach (var project in projectsToSearch) { - var documentsToSearch = await ReferenceFinders.Property.DetermineDocumentsToSearchAsync( + var documentsToSearch = await referenceFinder.DetermineDocumentsToSearchAsync( symbol, project, project.Documents.ToImmutableHashSet(), @@ -99,7 +102,7 @@ private static async Task> TrackAssignmentsAsync( { var syntaxFacts = document.GetRequiredLanguageService(); var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var referencesInDocument = await ReferenceFinders.Property.FindReferencesInDocumentAsync( + var referencesInDocument = await referenceFinder.FindReferencesInDocumentAsync( symbol, document, semanticModel, @@ -121,9 +124,13 @@ private static async Task> TrackFromPropertyAss var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var symbolInfo = semanticModel.GetSymbolInfo(valueTrackedItem.ExpressionNode); - if (symbolInfo.Symbol is not null) + var referenceFinder = GetReferenceFinder(symbolInfo.Symbol); + + if (referenceFinder is not null) { - var assignments = await TrackAssignmentsAsync(solution, symbolInfo.Symbol, cancellationToken).ConfigureAwait(false); + RoslynDebug.AssertNotNull(symbolInfo.Symbol); + + var assignments = await TrackAssignmentsAsync(solution, symbolInfo.Symbol, referenceFinder, cancellationToken).ConfigureAwait(false); return assignments .Select(a => new ValueTrackedItem(a.Location.Location, symbolInfo.Symbol, previousTrackedItem: valueTrackedItem)) .ToImmutableArray(); @@ -131,5 +138,14 @@ private static async Task> TrackFromPropertyAss return ImmutableArray.Empty; } + + private static IReferenceFinder? GetReferenceFinder(ISymbol? symbol) + => symbol switch + { + IPropertySymbol => ReferenceFinders.Property, + IFieldSymbol => ReferenceFinders.Field, + ILocalSymbol => ReferenceFinders.Local, + _ => null + }; } } diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs new file mode 100644 index 0000000000000..e96fb50a4c581 --- /dev/null +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ValueTracking; +using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.Shared.Extensions; +using System.Threading; +using Microsoft.CodeAnalysis.Text; +using Xunit; +using Microsoft.CodeAnalysis.Test.Utilities; + +namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking +{ + [UseExportProvider] + public class CSharpValueTrackingTests + { + private static async Task> GetTrackedItemsAsync(TestWorkspace testWorkspace, CancellationToken cancellationToken = default) + { + var cursorDocument = testWorkspace.DocumentWithCursor; + var document = testWorkspace.CurrentSolution.GetRequiredDocument(cursorDocument.Id); + var syntaxTree = await document.GetRequiredSyntaxTreeAsync(cancellationToken); + var textSpan = new TextSpan(cursorDocument.CursorPosition!.Value, 0); + var location = Location.Create(syntaxTree, textSpan); + var symbol = await GetSelectedSymbolAsync(textSpan, document, cancellationToken); + var service = testWorkspace.Services.GetRequiredService(); + return await service.TrackValueSourceAsync(testWorkspace.CurrentSolution, location, symbol, cancellationToken); + + } + + private static async Task GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) + { + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var selectedNode = root.FindNode(textSpan); + + Assert.NotNull(selectedNode); + + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var selectedSymbol = + semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol + ?? semanticModel.GetDeclaredSymbol(selectedNode, cancellationToken); + + Assert.NotNull(selectedSymbol); + return selectedSymbol!; + } + + [Fact] + public async Task TestPropertyAsync() + { + var code = +@" +class C +{ + public string $$S { get; set; } = """"""; + + public void SetS(string s) + { + S = s; + } + + public string GetS() => S; +} +"; + using var workspace = TestWorkspace.CreateCSharp(code); + var initialItems = await GetTrackedItemsAsync(workspace); + + Assert.Equal(2, initialItems.Length); + } + + [Fact] + public async Task TestFieldAsync() + { + var code = +@" +class C +{ + private string $$_s = """"""; + + public void SetS(string s) + { + _s = s; + } + + public string GetS() => _s; +}"; + using var workspace = TestWorkspace.CreateCSharp(code); + var initialItems = await GetTrackedItemsAsync(workspace); + + Assert.Equal(2, initialItems.Length); + } + } +} diff --git a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj index bf59d330743a2..329ab439a18f7 100644 --- a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj +++ b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj @@ -119,6 +119,9 @@ + + + diff --git a/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowCommandHandler.cs b/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowCommandHandler.cs deleted file mode 100644 index cf53b8a500771..0000000000000 --- a/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowCommandHandler.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.ComponentModel.Composition; -using Microsoft.CodeAnalysis.Editor; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.VisualStudio.Commanding; -using Microsoft.VisualStudio.Utilities; - -namespace Microsoft.VisualStudio.LanguageServices.DataFlowWindow -{ - [Export(typeof(ICommandHandler))] - [ContentType(ContentTypeNames.RoslynContentType)] - [Name(PredefinedCommandHandlerNames.GoToDataFlow)] - internal class DataFlowCommandHandler : ICommandHandler - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public DataFlowCommandHandler() - { - } - - public string DisplayName => "Go to data flow"; - - public bool ExecuteCommand(DataFlowEditorCommandArgs args, CommandExecutionContext executionContext) - { - throw new NotImplementedException(); - } - - public CommandState GetCommandState(DataFlowEditorCommandArgs args) - { - throw new NotImplementedException(); - } - } -} diff --git a/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowEditorCommandArgs.cs b/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowEditorCommandArgs.cs deleted file mode 100644 index 64d0a85908b5b..0000000000000 --- a/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowEditorCommandArgs.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Text.Editor.Commanding; - -namespace Microsoft.VisualStudio.LanguageServices.DataFlowWindow -{ - internal class DataFlowEditorCommandArgs : EditorCommandArgs - { - public DataFlowEditorCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer) - { - } - } -} diff --git a/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowToolWindow.cs b/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowToolWindow.cs deleted file mode 100644 index 325c47176f9d1..0000000000000 --- a/src/VisualStudio/Core/Def/DataFlowWindow/DataFlowToolWindow.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.InteropServices; -using System.Windows.Controls; -using Microsoft.VisualStudio.Shell; - -namespace Microsoft.VisualStudio.LanguageServices.DataFlowWindow -{ - [Guid("60a19d42-2dd7-43f3-be90-c7a9cb7d28f4")] - internal class DataFlowToolWindow : ToolWindowPane - { - public DataFlowToolWindow() - { - this.Caption = "Data Flow Tool Window"; - Content = new TextBlock() - { - Text = "Testing" - }; - } - } -} From 03999cc878d97adc5bd0942d2a1e27c9b5cd67f6 Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Fri, 12 Mar 2021 11:19:37 -0800 Subject: [PATCH 005/127] Remove package changes --- src/VisualStudio/Core/Def/Commands.vsct | 19 -- src/VisualStudio/Core/Def/Guids.cs | 3 - .../Core/Def/ID.RoslynCommands.cs | 1 - .../Def/Implementation/CommandBindings.cs | 5 - ...osoft.VisualStudio.LanguageServices.csproj | 5 +- .../Core/Def/PackageRegistration.pkgdef | 6 - src/VisualStudio/Core/Def/RoslynPackage.cs | 21 +-- .../ValueTrackingCommandHandler.cs | 174 ------------------ .../ValueTrackingEditorCommandArgs.cs | 22 --- .../ValueTracking/ValueTrackingToolWindow.cs | 58 ------ .../Def/ValueTracking/ValueTrackingTree.xaml | 72 -------- .../ValueTracking/ValueTrackingTree.xaml.cs | 35 ---- .../ValueTrackingTreeItemViewModel.cs | 163 ---------------- .../ValueTrackingTreeViewModel.cs | 37 ---- .../Core/Def/xlf/Commands.vsct.cs.xlf | 15 -- .../Core/Def/xlf/Commands.vsct.de.xlf | 15 -- .../Core/Def/xlf/Commands.vsct.es.xlf | 15 -- .../Core/Def/xlf/Commands.vsct.fr.xlf | 15 -- .../Core/Def/xlf/Commands.vsct.it.xlf | 15 -- .../Core/Def/xlf/Commands.vsct.ja.xlf | 15 -- .../Core/Def/xlf/Commands.vsct.ko.xlf | 15 -- .../Core/Def/xlf/Commands.vsct.pl.xlf | 15 -- .../Core/Def/xlf/Commands.vsct.pt-BR.xlf | 15 -- .../Core/Def/xlf/Commands.vsct.ru.xlf | 15 -- .../Core/Def/xlf/Commands.vsct.tr.xlf | 15 -- .../Core/Def/xlf/Commands.vsct.zh-Hans.xlf | 15 -- .../Core/Def/xlf/Commands.vsct.zh-Hant.xlf | 15 -- 27 files changed, 8 insertions(+), 808 deletions(-) delete mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs delete mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingEditorCommandArgs.cs delete mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs delete mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml delete mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs delete mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs delete mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs diff --git a/src/VisualStudio/Core/Def/Commands.vsct b/src/VisualStudio/Core/Def/Commands.vsct index 591fab85c9ba0..422b93c7d0bfd 100644 --- a/src/VisualStudio/Core/Def/Commands.vsct +++ b/src/VisualStudio/Core/Def/Commands.vsct @@ -411,19 +411,6 @@ RemoveUnusedReferences - - @@ -529,10 +516,6 @@ - - - - @@ -618,8 +601,6 @@ - - diff --git a/src/VisualStudio/Core/Def/Guids.cs b/src/VisualStudio/Core/Def/Guids.cs index 994acb0fbbebc..6f058e3c50002 100644 --- a/src/VisualStudio/Core/Def/Guids.cs +++ b/src/VisualStudio/Core/Def/Guids.cs @@ -125,9 +125,6 @@ internal static class Guids public static readonly Guid RoslynCommandSetId = new(RoslynCommandSetIdString); public static readonly Guid RoslynGroupId = new(RoslynGroupIdString); - public const string ValueTrackingToolWindowIdString = "60a19d42-2dd7-43f3-be90-c7a9cb7d28f4"; - public static readonly Guid ValueTrackingToolWindowId = new(ValueTrackingToolWindowIdString); - // TODO: Remove pending https://github.com/dotnet/roslyn/issues/8927 . // Interactive guids public const string InteractiveCommandSetIdString = "00B8868B-F9F5-4970-A048-410B05508506"; diff --git a/src/VisualStudio/Core/Def/ID.RoslynCommands.cs b/src/VisualStudio/Core/Def/ID.RoslynCommands.cs index ad65fd8e87b1f..d89285327286e 100644 --- a/src/VisualStudio/Core/Def/ID.RoslynCommands.cs +++ b/src/VisualStudio/Core/Def/ID.RoslynCommands.cs @@ -50,7 +50,6 @@ public static class RoslynCommands public const int RunCodeAnalysisForProject = 0x0201; public const int RemoveUnusedReferences = 0x0202; - public const int GoToValueTrackingWindow = 0x0203; } } } diff --git a/src/VisualStudio/Core/Def/Implementation/CommandBindings.cs b/src/VisualStudio/Core/Def/Implementation/CommandBindings.cs index b2992efa119ab..27a6efccbd0ea 100644 --- a/src/VisualStudio/Core/Def/Implementation/CommandBindings.cs +++ b/src/VisualStudio/Core/Def/Implementation/CommandBindings.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.Editor.Commanding.Commands; using Microsoft.VisualStudio.Editor.Commanding; using Microsoft.VisualStudio.LanguageServices; -using Microsoft.VisualStudio.LanguageServices.ValueTracking; namespace Microsoft.VisualStudio.Editor.Implementation { @@ -29,9 +28,5 @@ internal sealed class CommandBindings [Export] [CommandBinding(Guids.CSharpGroupIdString, ID.CSharpCommands.ContextOrganizeRemoveAndSort, typeof(SortAndRemoveUnnecessaryImportsCommandArgs))] internal CommandBindingDefinition contextOrganizeRemoveAndSortCommandBinding; - - [Export] - [CommandBinding(Guids.RoslynGroupIdString, ID.RoslynCommands.GoToValueTrackingWindow, typeof(ValueTrackingEditorCommandArgs))] - internal CommandBindingDefinition gotoDataFlowToolCommandBinding; } } diff --git a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj index 1eb93e319b038..5fab552e80282 100644 --- a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj +++ b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj @@ -33,7 +33,7 @@ 6cf2e545-6109-4730-8883-cf43d7aec3e1 - + @@ -199,7 +199,8 @@ - + diff --git a/src/VisualStudio/Core/Def/PackageRegistration.pkgdef b/src/VisualStudio/Core/Def/PackageRegistration.pkgdef index d1b8281f52747..27e41b380e9ba 100644 --- a/src/VisualStudio/Core/Def/PackageRegistration.pkgdef +++ b/src/VisualStudio/Core/Def/PackageRegistration.pkgdef @@ -24,9 +24,3 @@ "Value"=dword:00000000 "Title"="Enable experimental C#/VB LSP completion experience" "PreviewPaneChannels"="IntPreview,int.main" - -// The option page configuration is duplicated in RoslynPackage -// [ProvideToolWindow(typeof(ValueTracking.ValueTrackingToolWindow))] -[$RootKey$\ToolWindows\{60a19d42-2dd7-43f3-be90-c7a9cb7d28f4}] -"Name"="Microsoft.VisualStudio.LanguageServices.ValueTracking.ValueTrackingToolWindow" -@="{6cf2e545-6109-4730-8883-cf43d7aec3e1}" diff --git a/src/VisualStudio/Core/Def/RoslynPackage.cs b/src/VisualStudio/Core/Def/RoslynPackage.cs index 57e373566a571..7261ce5c45d59 100644 --- a/src/VisualStudio/Core/Def/RoslynPackage.cs +++ b/src/VisualStudio/Core/Def/RoslynPackage.cs @@ -19,23 +19,28 @@ using Microsoft.CodeAnalysis.Logging; using Microsoft.CodeAnalysis.Notification; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.CodeAnalysis.Versions; using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.LanguageServices.ColorSchemes; using Microsoft.VisualStudio.LanguageServices.Experimentation; using Microsoft.VisualStudio.LanguageServices.Implementation; +using Microsoft.VisualStudio.LanguageServices.Implementation.DesignerAttribute; using Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics; using Microsoft.VisualStudio.LanguageServices.Implementation.Interactive; using Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.RuleSets; +using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectTelemetry; using Microsoft.VisualStudio.LanguageServices.Implementation.TableDataSource; +using Microsoft.VisualStudio.LanguageServices.Implementation.TodoComments; using Microsoft.VisualStudio.LanguageServices.Implementation.UnusedReferences; using Microsoft.VisualStudio.LanguageServices.Telemetry; using Microsoft.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Shell.ServiceBroker; using Microsoft.VisualStudio.TaskStatusCenter; using Microsoft.VisualStudio.Telemetry; using Microsoft.VisualStudio.TextManager.Interop; @@ -46,9 +51,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Setup { [Guid(Guids.RoslynPackageIdString)] - - // The option page configuration is duplicated in PackageRegistration.pkgdef - [ProvideToolWindow(typeof(ValueTracking.ValueTrackingToolWindow))] internal sealed class RoslynPackage : AbstractPackage { // The randomly-generated key name is used for serializing the ILSpy decompiler EULA preference to the .SUO @@ -186,19 +188,6 @@ protected override async Task LoadComponentsAsync(CancellationToken cancellation LoadComponentsBackgroundAsync(cancellationToken).Forget(); } - // Overrides for VSSDK003 fix - // See https://github.com/Microsoft/VSSDK-Analyzers/blob/main/doc/VSSDK003.md - public override IVsAsyncToolWindowFactory GetAsyncToolWindowFactory(Guid toolWindowType) - => toolWindowType == typeof(ValueTracking.ValueTrackingToolWindow).GUID - ? this - : base.GetAsyncToolWindowFactory(toolWindowType); - - protected override string GetToolWindowTitle(Type toolWindowType, int id) - => base.GetToolWindowTitle(toolWindowType, id); - - protected override Task InitializeToolWindowAsync(Type toolWindowType, int id, CancellationToken cancellationToken) - => Task.FromResult((object?)null); - private async Task LoadComponentsBackgroundAsync(CancellationToken cancellationToken) { await TaskScheduler.Default; diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs deleted file mode 100644 index 2bcbb7f1463eb..0000000000000 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs +++ /dev/null @@ -1,174 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.ComponentModel.Composition; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Editor; -using Microsoft.CodeAnalysis.Editor.Shared.Extensions; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.LanguageServices; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.ValueTracking; -using Microsoft.VisualStudio.Commanding; -using Microsoft.VisualStudio.GraphModel.CodeSchema; -using Microsoft.VisualStudio.LanguageServices.Setup; -using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Shell.Interop; -using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Utilities; -using IAsyncServiceProvider = Microsoft.VisualStudio.Shell.IAsyncServiceProvider; -using Task = System.Threading.Tasks.Task; - -namespace Microsoft.VisualStudio.LanguageServices.ValueTracking -{ - [Export(typeof(ICommandHandler))] - [ContentType(ContentTypeNames.RoslynContentType)] - [Name(PredefinedCommandHandlerNames.ShowValueTracking)] - internal class ValueTrackingCommandHandler : ICommandHandler - { - private readonly IAsyncServiceProvider _serviceProvider; - private readonly IThreadingContext _threadingContext; - - private RoslynPackage? _roslynPackage; - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public ValueTrackingCommandHandler(SVsServiceProvider serviceProvider, IThreadingContext threadingContext) - { - _serviceProvider = (IAsyncServiceProvider)serviceProvider; - _threadingContext = threadingContext; - } - - public string DisplayName => "Go to value tracking"; - - public CommandState GetCommandState(ValueTrackingEditorCommandArgs args) - => CommandState.Available; - - public bool ExecuteCommand(ValueTrackingEditorCommandArgs args, CommandExecutionContext executionContext) - { - var cancellationToken = executionContext.OperationContext.UserCancellationToken; - var caretPosition = args.TextView.GetCaretPoint(args.SubjectBuffer); - if (!caretPosition.HasValue) - { - return false; - } - - var textSpan = new TextSpan(caretPosition.Value.Position, 0); - var sourceTextContainer = args.SubjectBuffer.AsTextContainer(); - var document = sourceTextContainer.GetOpenDocumentInCurrentContext(); - if (document is null) - { - return false; - } - - var selectedSymbol = _threadingContext.JoinableTaskFactory.Run(() => GetSelectedSymbolAsync(textSpan, document, cancellationToken)); - if (selectedSymbol is null) - { - return false; - } - - var syntaxTree = document.GetRequiredSyntaxTreeSynchronously(cancellationToken); - var location = Location.Create(syntaxTree, textSpan); - - _threadingContext.JoinableTaskFactory.Run(() => ShowToolWindowAsync(selectedSymbol, location, document.Project.Solution, cancellationToken)); - - return true; - } - - private static async Task GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) - { - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var selectedNode = root.FindNode(textSpan); - if (selectedNode is null) - { - return null; - } - - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var selectedSymbol = - semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol - ?? semanticModel.GetDeclaredSymbol(selectedNode, cancellationToken); - - if (selectedSymbol is null) - { - return null; - } - - return selectedSymbol switch - { - ILocalSymbol - or IPropertySymbol { SetMethod: not null } - or IFieldSymbol { IsReadOnly: false } - or IEventSymbol - or IParameterSymbol - => selectedSymbol, - - _ => null - }; - } - - private async Task ShowToolWindowAsync(ISymbol selectedSymbol, Location location, Solution solution, CancellationToken cancellationToken) - { - var roslynPackage = await TryGetRoslynPackageAsync(cancellationToken).ConfigureAwait(false); - - if (roslynPackage is null) - { - return; - } - - var dataFlowItem = new ValueTrackingTreeItemViewModel( - new ValueTrackedItem(location, selectedSymbol), - solution, - solution.Workspace.Services.GetRequiredService()); - - if (ValueTrackingToolWindow.Instance is null) - { - var factory = roslynPackage.GetAsyncToolWindowFactory(Guids.ValueTrackingToolWindowId); - - factory.CreateToolWindow(Guids.ValueTrackingToolWindowId, 0, dataFlowItem); - await factory.InitializeToolWindowAsync(Guids.ValueTrackingToolWindowId, 0); - - ValueTrackingToolWindow.Instance = (ValueTrackingToolWindow)await roslynPackage.ShowToolWindowAsync( - typeof(ValueTrackingToolWindow), - 0, - true, - roslynPackage.DisposalToken).ConfigureAwait(false); - } - else - { - ValueTrackingToolWindow.Instance.Root = dataFlowItem; - await roslynPackage.ShowToolWindowAsync( - typeof(ValueTrackingToolWindow), - 0, - false, - roslynPackage.DisposalToken).ConfigureAwait(false); - } - } - - private async ValueTask TryGetRoslynPackageAsync(CancellationToken cancellationToken) - { - if (_roslynPackage is null) - { - await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - - var shell = (IVsShell7?)await _serviceProvider.GetServiceAsync(typeof(SVsShell)).ConfigureAwait(true); - Assumes.Present(shell); - await shell.LoadPackageAsync(typeof(RoslynPackage).GUID); - - if (ErrorHandler.Succeeded(((IVsShell)shell).IsPackageLoaded(typeof(RoslynPackage).GUID, out var package))) - { - _roslynPackage = (RoslynPackage)package; - } - } - - return _roslynPackage; - } - } -} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingEditorCommandArgs.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingEditorCommandArgs.cs deleted file mode 100644 index 75e7930090a3a..0000000000000 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingEditorCommandArgs.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Text.Editor.Commanding; - -namespace Microsoft.VisualStudio.LanguageServices.ValueTracking -{ - internal class ValueTrackingEditorCommandArgs : EditorCommandArgs - { - public ValueTrackingEditorCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer) - { - } - } -} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs deleted file mode 100644 index 67c24e9cf3d5e..0000000000000 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Windows; -using System.Windows.Controls; -using Microsoft.CodeAnalysis; -using Microsoft.VisualStudio.Shell; - -namespace Microsoft.VisualStudio.LanguageServices.ValueTracking -{ - [Guid(Guids.ValueTrackingToolWindowIdString)] - internal class ValueTrackingToolWindow : ToolWindowPane - { - public static ValueTrackingToolWindow? Instance { get; set; } - private readonly ValueTrackingTreeViewModel _viewModel; - - // Needed for VSSDK003 - // See https://github.com/Microsoft/VSSDK-Analyzers/blob/main/doc/VSSDK003.md - public ValueTrackingToolWindow(object o) - : base(null) - { - if (Instance is not null) - { - throw new Exception("Cannot initialize the window more than once"); - } - - this.Caption = "Value Tracking"; - - if (o is ValueTrackingTreeItemViewModel root) - { - _viewModel = new ValueTrackingTreeViewModel(root); - Content = new ValueTrackingTree(_viewModel); - } - else - { - throw new Exception("This shouldn't happen"); - } - } - - public ValueTrackingTreeItemViewModel Root - { - get => _viewModel.Roots.Single(); - set - { - _viewModel.Roots.Clear(); - _viewModel.Roots.Add(value); - } - } - } -} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml deleted file mode 100644 index 2b19c1bce37bd..0000000000000 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs deleted file mode 100644 index 87ee81bb63c8b..0000000000000 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace Microsoft.VisualStudio.LanguageServices.ValueTracking -{ - /// - /// Interaction logic for ValueTrackingTree.xaml - /// - internal partial class ValueTrackingTree : UserControl - { - private readonly ValueTrackingTreeViewModel _viewModel; - - public ValueTrackingTree(ValueTrackingTreeViewModel viewModel) - { - DataContext = _viewModel = viewModel; - InitializeComponent(); - } - } -} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs deleted file mode 100644 index a82eb4936d020..0000000000000 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs +++ /dev/null @@ -1,163 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.IO; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.ValueTracking; -using Roslyn.Utilities; - -namespace Microsoft.VisualStudio.LanguageServices.ValueTracking -{ - internal class TreeViewItemBase : INotifyPropertyChanged - { - private bool _isExpanded = false; - public bool IsNodeExpanded - { - get => _isExpanded; - set => SetProperty(ref _isExpanded, value); - } - - private bool _isSelected = false; - public bool IsNodeSelected - { - get => _isSelected; - set => SetProperty(ref _isSelected, value); - } - - public event PropertyChangedEventHandler? PropertyChanged; - - protected void SetProperty(ref T field, T value, [CallerMemberName] string name = "") - { - if (EqualityComparer.Default.Equals(field, value)) - { - return; - } - - field = value; - NotifyPropertyChanged(name); - } - - protected void NotifyPropertyChanged(string name) - => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); - } - internal class EmptyTreeViewItem : TreeViewItemBase - { - public static EmptyTreeViewItem Instance { get; } = new(); - - private EmptyTreeViewItem() - { - } - } - - internal class ValueTrackingTreeItemViewModel : TreeViewItemBase - { - private bool _childrenCalculated; - private readonly Solution _solution; - private readonly IValueTrackingService _valueTrackingService; - - public ObservableCollection ChildItems { get; } = new(); - - public Location Location => TrackedItem.Location; - public ISymbol Symbol => TrackedItem.Symbol; - - public string FileName => Path.GetFileName(Location.SourceTree!.FilePath); - public string LineDisplay - { - get - { - var linePosition = Location.GetLineSpan().StartLinePosition; - return linePosition.Line.ToString(); - } - } - - public string SymbolName => Symbol.ToDisplayString(); - public string ContainerName => Symbol.ContainingSymbol.ToDisplayString(); - - public ValueTrackedItem TrackedItem { get; } - - public ValueTrackingTreeItemViewModel( - ValueTrackedItem trackedItem, - Solution solution, - IValueTrackingService valueTrackingService) - { - Contract.ThrowIfFalse(trackedItem.Location.IsInSource); - - ChildItems.Add(EmptyTreeViewItem.Instance); - - TrackedItem = trackedItem; - _solution = solution; - _valueTrackingService = valueTrackingService; - - ChildItems.CollectionChanged += (s, a) => - { - NotifyPropertyChanged(nameof(ChildItems)); - }; - - PropertyChanged += (s, a) => - { - if (a.PropertyName == nameof(IsNodeExpanded)) - { - if (_childrenCalculated) - { - return; - } - - _childrenCalculated = true; - - // TODO: What cancellationtoken to use here? - CalculateChildrenAsync(CancellationToken.None) - .ContinueWith(task => - { - if (task.Exception is not null) - { - _childrenCalculated = false; - return; - } - - ChildItems.Clear(); - - foreach (var item in task.Result) - { - ChildItems.Add(item); - } - }, - - // Use the UI thread synchronization context for calling back to the UI - // thread to add the tiems - TaskScheduler.FromCurrentSynchronizationContext()); - - } - }; - } - - private async Task> CalculateChildrenAsync(CancellationToken cancellationToken) - { - var valueTrackedItems = await _valueTrackingService.TrackValueSourceAsync( - _solution, - TrackedItem, - cancellationToken).ConfigureAwait(false); - - // TODO: Use pooled item here? - var builder = new List(); - foreach (var valueTrackedItem in valueTrackedItems) - { - builder.Add(new ValueTrackingTreeItemViewModel( - valueTrackedItem, - _solution, - _valueTrackingService)); - } - - return builder.ToImmutableArray(); - } - } -} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs deleted file mode 100644 index 486bb1ec71c90..0000000000000 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Runtime.CompilerServices; - -namespace Microsoft.VisualStudio.LanguageServices.ValueTracking -{ - internal class ValueTrackingTreeViewModel : INotifyPropertyChanged - { - public ValueTrackingTreeViewModel(ValueTrackingTreeItemViewModel root) - { - Roots.Add(root); - } - - public ObservableCollection Roots { get; } = new(); - - public event PropertyChangedEventHandler? PropertyChanged; - - private void SetProperty(ref T field, T value, [CallerMemberName] string name = "") - { - if (EqualityComparer.Default.Equals(field, value)) - { - return; - } - - field = value; - NotifyPropertyChanged(name); - } - - private void NotifyPropertyChanged(string name) - => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); - } -} diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf index 2e5317d02b9e6..6ab46a153fc53 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf @@ -422,21 +422,6 @@ Přejít na implementaci - - Show Value Tracking - Show Value Tracking - - - - ShowValueTrackingCommandName - ShowValueTrackingCommandName - - - - ViewEditorConfigSettings - ViewEditorConfigSettings - - Initialize Interactive with Project Inicializovat Interactive přes projekt diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf index 4027a95cb4251..39691247a87d9 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf @@ -422,21 +422,6 @@ Zur Implementierung wechseln - - Show Value Tracking - Show Value Tracking - - - - ShowValueTrackingCommandName - ShowValueTrackingCommandName - - - - ViewEditorConfigSettings - ViewEditorConfigSettings - - Initialize Interactive with Project Interaktiv mit dem Projekt initialisieren diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf index 927958975d1ed..63941e8dbc88d 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf @@ -422,21 +422,6 @@ Ir a la implementación - - Show Value Tracking - Show Value Tracking - - - - ShowValueTrackingCommandName - ShowValueTrackingCommandName - - - - ViewEditorConfigSettings - ViewEditorConfigSettings - - Initialize Interactive with Project Inicializar el elemento interactivo con el proyecto diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf index a7b29c442ae2c..5eaa9a71bec58 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf @@ -422,21 +422,6 @@ Accéder à l'implémentation - - Show Value Tracking - Show Value Tracking - - - - ShowValueTrackingCommandName - ShowValueTrackingCommandName - - - - ViewEditorConfigSettings - ViewEditorConfigSettings - - Initialize Interactive with Project Initialiser Interactive avec le projet diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf index d0f71b5ac9223..bccc689169acb 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf @@ -422,21 +422,6 @@ Vai all'implementazione - - Show Value Tracking - Show Value Tracking - - - - ShowValueTrackingCommandName - ShowValueTrackingCommandName - - - - ViewEditorConfigSettings - ViewEditorConfigSettings - - Initialize Interactive with Project Inizializza Interactive con il progetto diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf index ad582208aea51..2ce24c0fc615d 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf @@ -422,21 +422,6 @@ 実装に移動 - - Show Value Tracking - Show Value Tracking - - - - ShowValueTrackingCommandName - ShowValueTrackingCommandName - - - - ViewEditorConfigSettings - ViewEditorConfigSettings - - Initialize Interactive with Project プロジェクトでインタラクティブを初期化 diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf index bd4c80e62708c..fed9ec757ba77 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf @@ -422,21 +422,6 @@ 구현으로 이동 - - Show Value Tracking - Show Value Tracking - - - - ShowValueTrackingCommandName - ShowValueTrackingCommandName - - - - ViewEditorConfigSettings - ViewEditorConfigSettings - - Initialize Interactive with Project 프로젝트에서 Interactive 초기화 diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf index 6eeca8d5d5a35..bb907800c0146 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf @@ -422,21 +422,6 @@ Przejdź do implementacji - - Show Value Tracking - Show Value Tracking - - - - ShowValueTrackingCommandName - ShowValueTrackingCommandName - - - - ViewEditorConfigSettings - ViewEditorConfigSettings - - Initialize Interactive with Project Inicjuj środowisko interaktywne z projektem diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf index fdc3219090701..5b6cd2c43c66c 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf @@ -422,21 +422,6 @@ Ir para Implementação - - Show Value Tracking - Show Value Tracking - - - - ShowValueTrackingCommandName - ShowValueTrackingCommandName - - - - ViewEditorConfigSettings - ViewEditorConfigSettings - - Initialize Interactive with Project Inicializar Interativo com o Projeto diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf index ec8809be173ed..d68b6204e131e 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf @@ -422,21 +422,6 @@ Перейти к реализации - - Show Value Tracking - Show Value Tracking - - - - ShowValueTrackingCommandName - ShowValueTrackingCommandName - - - - ViewEditorConfigSettings - ViewEditorConfigSettings - - Initialize Interactive with Project Инициализировать интерактивное окно с проектом diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf index 4190512bc5068..1d0204a6df606 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf @@ -422,21 +422,6 @@ Uygulamaya Git - - Show Value Tracking - Show Value Tracking - - - - ShowValueTrackingCommandName - ShowValueTrackingCommandName - - - - ViewEditorConfigSettings - ViewEditorConfigSettings - - Initialize Interactive with Project Projeyi Etkileşimli Pencerede Başlat diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf index 984d36ecf7968..316d4be560b9c 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf @@ -422,21 +422,6 @@ 转到实现 - - Show Value Tracking - Show Value Tracking - - - - ShowValueTrackingCommandName - ShowValueTrackingCommandName - - - - ViewEditorConfigSettings - ViewEditorConfigSettings - - Initialize Interactive with Project 对交互窗口进行项目初始化 diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf index 223c86fd530dc..c01b09934ee99 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf @@ -422,21 +422,6 @@ 前往實作 - - Show Value Tracking - Show Value Tracking - - - - ShowValueTrackingCommandName - ShowValueTrackingCommandName - - - - ViewEditorConfigSettings - ViewEditorConfigSettings - - Initialize Interactive with Project 使用專案將 Interactive 初始化 From 085e7c3b3c96e705be9593a0ce346ac8287774d6 Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Mon, 15 Mar 2021 15:06:50 -0700 Subject: [PATCH 006/127] WIP --- .../ValueTracking/ValueTrackingService.cs | 90 ++++++++----------- .../ValueTracking/CSharpValueTrackingTests.cs | 23 ++++- 2 files changed, 58 insertions(+), 55 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 84334730010b1..266f857b1f155 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -43,51 +43,27 @@ public async Task> TrackValueSourceAsync( } var assignments = await TrackAssignmentsAsync(solution, symbol, referneceFinder, cancellationToken).ConfigureAwait(false); - return assignments.Select(a => new ValueTrackedItem(a.Location.Location, symbol)).ToImmutableArray(); + return assignments.Select(l => new ValueTrackedItem(l, symbol)).ToImmutableArray(); } - public async Task> TrackValueSourceAsync( + public Task> TrackValueSourceAsync( Solution solution, ValueTrackedItem previousTrackedItem, CancellationToken cancellationToken) { - RoslynDebug.AssertNotNull(previousTrackedItem.Location.SourceTree); - - if (previousTrackedItem.PreviousTrackedItem is null) - { - var symbol = previousTrackedItem.Symbol; - var referenceFinder = GetReferenceFinder(symbol); - if (referenceFinder is null) - { - return ImmutableArray.Empty; - } - - var assignments = await TrackAssignmentsAsync(solution, symbol, referenceFinder, cancellationToken).ConfigureAwait(false); - return assignments.Select(a => new ValueTrackedItem(a.Location.Location, symbol, previousTrackedItem: previousTrackedItem)).ToImmutableArray(); - } - - // There's no interesting node - if (previousTrackedItem.ExpressionNode is null) - { - return ImmutableArray.Empty; - } - - if (previousTrackedItem.Symbol is IPropertySymbol) - { - var document = solution.GetRequiredDocument(previousTrackedItem.Location.SourceTree); - return await TrackFromPropertyAssignmentAsync(solution, document, previousTrackedItem, cancellationToken).ConfigureAwait(false); - } - - throw new Exception(); + throw new NotImplementedException(); } - private static async Task> TrackAssignmentsAsync( + private static async Task> TrackAssignmentsAsync( Solution solution, ISymbol symbol, IReferenceFinder referenceFinder, CancellationToken cancellationToken) { - using var _ = PooledObjects.ArrayBuilder.GetInstance(out var builder); + using var _ = PooledObjects.ArrayBuilder.GetInstance(out var builder); + + // Add all the references to the symbol + // that write to it var projectsToSearch = await referenceFinder.DetermineProjectsToSearchAsync(symbol, solution, solution.Projects.ToImmutableHashSet(), cancellationToken).ConfigureAwait(false); foreach (var project in projectsToSearch) { @@ -109,34 +85,46 @@ private static async Task> TrackAssignmentsAsync( FindReferencesSearchOptions.Default, cancellationToken).ConfigureAwait(false); - builder.AddRange(referencesInDocument - .Where(r => r.Location.IsWrittenTo)); + foreach (var reference in referencesInDocument.Where(r => r.Location.IsWrittenTo)) + { + AddAssignment(reference.Node, syntaxFacts); + } + + //builder.AddRange( + // referencesInDocument + // .Where(r => r.Location.IsWrittenTo) + // .Select(r => r.Location.Location)); } } - return builder.AsImmutableOrEmpty(); - } - - private static async Task> TrackFromPropertyAssignmentAsync(Solution solution, Document document, ValueTrackedItem valueTrackedItem, CancellationToken cancellationToken) - { - RoslynDebug.AssertNotNull(valueTrackedItem.ExpressionNode); + // Add all initializations of the symbol. Those are not caught in + // the reference finder but should still show up in the tree + foreach (var location in symbol.Locations.Where(location => location.IsInSource)) + { + var node = location.FindNode(cancellationToken); + var document = solution.GetRequiredDocument(location.SourceTree); + var syntaxFacts = document.GetRequiredLanguageService(); - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var symbolInfo = semanticModel.GetSymbolInfo(valueTrackedItem.ExpressionNode); + AddAssignment(node, syntaxFacts); + } - var referenceFinder = GetReferenceFinder(symbolInfo.Symbol); + return builder.AsImmutableOrEmpty(); - if (referenceFinder is not null) + void AddAssignment(SyntaxNode node, ISyntaxFactsService syntaxFacts) { - RoslynDebug.AssertNotNull(symbolInfo.Symbol); + if (syntaxFacts.IsDeclaration(node)) + { + builder.Add(node.GetLocation()); + return; + } - var assignments = await TrackAssignmentsAsync(solution, symbolInfo.Symbol, referenceFinder, cancellationToken).ConfigureAwait(false); - return assignments - .Select(a => new ValueTrackedItem(a.Location.Location, symbolInfo.Symbol, previousTrackedItem: valueTrackedItem)) - .ToImmutableArray(); + var assignment = node.FirstAncestorOrSelf(syntaxFacts.IsLeftSideOfAnyAssignment); + if (assignment is not null && assignment.Parent is not null) + { + builder.Add(assignment.Parent.GetLocation()); + return; + } } - - return ImmutableArray.Empty; } private static IReferenceFinder? GetReferenceFinder(ISymbol? symbol) diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs index e96fb50a4c581..342c5cbf5be1a 100644 --- a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ValueTracking; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; @@ -15,6 +11,7 @@ using Microsoft.CodeAnalysis.Text; using Xunit; using Microsoft.CodeAnalysis.Test.Utilities; +using System.Windows.Documents; namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking { @@ -50,6 +47,20 @@ private static async Task GetSelectedSymbolAsync(TextSpan textSpan, Doc return selectedSymbol!; } + private static string GetText(ValueTrackedItem item) + { + var sourceTree = item.Location.SourceTree; + var span = item.Location.SourceSpan; + + Assert.NotNull(sourceTree); + if (sourceTree!.TryGetText(out var text)) + { + return text!.GetSubText(span).ToString(); + } + + return sourceTree!.ToString(); + } + [Fact] public async Task TestPropertyAsync() { @@ -71,6 +82,8 @@ public void SetS(string s) var initialItems = await GetTrackedItemsAsync(workspace); Assert.Equal(2, initialItems.Length); + Assert.Equal("S = s", GetText(initialItems[0])); + Assert.Equal(@"public string S { get; set; } = """"", GetText(initialItems[1])); } [Fact] @@ -93,6 +106,8 @@ public void SetS(string s) var initialItems = await GetTrackedItemsAsync(workspace); Assert.Equal(2, initialItems.Length); + Assert.Equal("_s = s", GetText(initialItems[0])); + Assert.Equal(@"private string $$_s = """"", GetText(initialItems[1])); } } } From 7920278debd90202747e13855be4ea7493f23f92 Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Mon, 15 Mar 2021 19:37:10 -0700 Subject: [PATCH 007/127] Add more tests --- .../ValueTracking/ValueTrackingService.cs | 19 +-- .../ValueTracking/CSharpValueTrackingTests.cs | 114 +++++++++++++++++- 2 files changed, 122 insertions(+), 11 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 266f857b1f155..7f5f39dedb6cc 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -61,7 +61,7 @@ private static async Task> TrackAssignmentsAsync( CancellationToken cancellationToken) { using var _ = PooledObjects.ArrayBuilder.GetInstance(out var builder); - + // Add all the references to the symbol // that write to it var projectsToSearch = await referenceFinder.DetermineProjectsToSearchAsync(symbol, solution, solution.Projects.ToImmutableHashSet(), cancellationToken).ConfigureAwait(false); @@ -89,11 +89,6 @@ private static async Task> TrackAssignmentsAsync( { AddAssignment(reference.Node, syntaxFacts); } - - //builder.AddRange( - // referencesInDocument - // .Where(r => r.Location.IsWrittenTo) - // .Select(r => r.Location.Location)); } } @@ -101,6 +96,8 @@ private static async Task> TrackAssignmentsAsync( // the reference finder but should still show up in the tree foreach (var location in symbol.Locations.Where(location => location.IsInSource)) { + RoslynDebug.AssertNotNull(location.SourceTree); + var node = location.FindNode(cancellationToken); var document = solution.GetRequiredDocument(location.SourceTree); var syntaxFacts = document.GetRequiredLanguageService(); @@ -112,12 +109,19 @@ private static async Task> TrackAssignmentsAsync( void AddAssignment(SyntaxNode node, ISyntaxFactsService syntaxFacts) { - if (syntaxFacts.IsDeclaration(node)) + if (syntaxFacts.IsDeclaration(node) + || syntaxFacts.IsParameter(node)) { builder.Add(node.GetLocation()); return; } + if (syntaxFacts.IsVariableDeclarator(node) && node.Parent is not null) + { + builder.Add(node.Parent.GetLocation()); + return; + } + var assignment = node.FirstAncestorOrSelf(syntaxFacts.IsLeftSideOfAnyAssignment); if (assignment is not null && assignment.Parent is not null) { @@ -133,6 +137,7 @@ void AddAssignment(SyntaxNode node, ISyntaxFactsService syntaxFacts) IPropertySymbol => ReferenceFinders.Property, IFieldSymbol => ReferenceFinders.Field, ILocalSymbol => ReferenceFinders.Local, + IParameterSymbol => ReferenceFinders.Parameter, _ => null }; } diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs index 342c5cbf5be1a..57896af4cdfb4 100644 --- a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Text; using Xunit; using Microsoft.CodeAnalysis.Test.Utilities; -using System.Windows.Documents; namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking { @@ -62,7 +61,7 @@ private static string GetText(ValueTrackedItem item) } [Fact] - public async Task TestPropertyAsync() + public async Task TestProperty() { var code = @" @@ -87,7 +86,7 @@ public void SetS(string s) } [Fact] - public async Task TestFieldAsync() + public async Task TestField() { var code = @" @@ -107,7 +106,114 @@ public void SetS(string s) Assert.Equal(2, initialItems.Length); Assert.Equal("_s = s", GetText(initialItems[0])); - Assert.Equal(@"private string $$_s = """"", GetText(initialItems[1])); + + // This is displaying the initializer but not the full line + // TODO: Fix this to be the whole line? + Assert.Equal(@"_s = """"", GetText(initialItems[1])); + } + + [Fact] + public async Task TestLocal() + { + var code = +@" +class C +{ + public int Add(int x, int y) + { + var $$z = x; + z += y; + return z; + } +}"; + using var workspace = TestWorkspace.CreateCSharp(code); + var initialItems = await GetTrackedItemsAsync(workspace); + + Assert.Equal(2, initialItems.Length); + Assert.Equal(@"z += y", GetText(initialItems[0])); + Assert.Equal("var z = x", GetText(initialItems[1])); + } + + [Fact] + public async Task TestParameter() + { + var code = +@" +class C +{ + public int Add(int $$x, int y) + { + x += y; + return x; + } +}"; + using var workspace = TestWorkspace.CreateCSharp(code); + var initialItems = await GetTrackedItemsAsync(workspace); + + Assert.Equal(2, initialItems.Length); + Assert.Equal(@"x += y", GetText(initialItems[0])); + + // This is not whole line, but shows the full variable definition. + // Display may need to adjust for this, but the service is providing + // the correct information here + Assert.Equal("int x", GetText(initialItems[1])); + } + + [Fact] + public async Task TestMissingOnMethod() + { + var code = +@" +class C +{ + public int $$Add(int x, int y) + { + x += y; + return x; + } +}"; + using var workspace = TestWorkspace.CreateCSharp(code); + var initialItems = await GetTrackedItemsAsync(workspace); + Assert.Empty(initialItems); + } + + [Fact] + public async Task TestMissingOnClass() + { + var code = +@" +class $$C +{ + public int Add(int x, int y) + { + x += y; + return x; + } +}"; + using var workspace = TestWorkspace.CreateCSharp(code); + var initialItems = await GetTrackedItemsAsync(workspace); + Assert.Empty(initialItems); + } + + [Fact] + public async Task TestMissingOnNamespace() + { + var code = +@" +namespace $$N +{ + class C + { + public int Add(int x, int y) + { + x += y; + return x; + } + } +}"; + using var workspace = TestWorkspace.CreateCSharp(code); + var initialItems = await GetTrackedItemsAsync(workspace); + Assert.Empty(initialItems); } } } From 2906847aec168dc3304d34302e0eb7667329e05e Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Mon, 15 Mar 2021 19:49:17 -0700 Subject: [PATCH 008/127] Undo unneeded changes --- .../Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj | 1 - .../Extensibility/Commands/PredefinedCommandHandlerNames.cs | 5 ----- .../Core/Portable/Microsoft.CodeAnalysis.Features.csproj | 3 --- 3 files changed, 9 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj b/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj index 19a867ba77c57..4211ba99baff2 100644 --- a/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj +++ b/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj @@ -69,7 +69,6 @@ - diff --git a/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.cs b/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.cs index 531132d451939..545e53524b7d7 100644 --- a/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.cs +++ b/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.cs @@ -178,10 +178,5 @@ internal static class PredefinedCommandHandlerNames /// Command handler name for Edit and Continue file save handler. /// public const string EditAndContinueFileSave = "Edit and Continue Save File Handler"; - - /// - /// Command handler name for showing the Value Tracking tool window. - /// - public const string ShowValueTracking = "Show Value Tracking"; } } diff --git a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj index 329ab439a18f7..bf59d330743a2 100644 --- a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj +++ b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj @@ -119,9 +119,6 @@ - - - From 1dd8dc3b1094cffcc9cee6f34261b3c9295747aa Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Tue, 16 Mar 2021 00:33:26 -0700 Subject: [PATCH 009/127] Add comments to test. Make abstract base for tests in prep for VB --- .../Core/ValueTracking/ValueTrackedItem.cs | 8 +-- .../AbstractBaseValueTrackingTests.cs | 62 ++++++++++++++++++ .../ValueTracking/CSharpValueTrackingTests.cs | 65 ++++++------------- 3 files changed, 84 insertions(+), 51 deletions(-) create mode 100644 src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs index 46247c807d43f..0c8bd812e5b38 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs @@ -8,19 +8,13 @@ internal class ValueTrackedItem { public Location Location { get; } public ISymbol Symbol { get; } - public SyntaxNode? ExpressionNode { get; } - public ValueTrackedItem? PreviousTrackedItem { get; } public ValueTrackedItem( Location location, - ISymbol symbol, - SyntaxNode? expressionNode = null, - ValueTrackedItem? previousTrackedItem = null) + ISymbol symbol) { Location = location; Symbol = symbol; - ExpressionNode = expressionNode; - PreviousTrackedItem = previousTrackedItem; } } } diff --git a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs new file mode 100644 index 0000000000000..aaba47a3951c5 --- /dev/null +++ b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ValueTracking; +using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.Shared.Extensions; +using System.Threading; +using Microsoft.CodeAnalysis.Text; +using Xunit; +using Microsoft.CodeAnalysis.Test.Utilities; + +namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking +{ + internal abstract class AbstractBaseValueTrackingTests + { + protected static async Task> GetTrackedItemsAsync(TestWorkspace testWorkspace, CancellationToken cancellationToken = default) + { + var cursorDocument = testWorkspace.DocumentWithCursor; + var document = testWorkspace.CurrentSolution.GetRequiredDocument(cursorDocument.Id); + var syntaxTree = await document.GetRequiredSyntaxTreeAsync(cancellationToken); + var textSpan = new TextSpan(cursorDocument.CursorPosition!.Value, 0); + var location = Location.Create(syntaxTree, textSpan); + var symbol = await GetSelectedSymbolAsync(textSpan, document, cancellationToken); + var service = testWorkspace.Services.GetRequiredService(); + return await service.TrackValueSourceAsync(testWorkspace.CurrentSolution, location, symbol, cancellationToken); + + } + + protected static async Task GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) + { + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var selectedNode = root.FindNode(textSpan); + + Assert.NotNull(selectedNode); + + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var selectedSymbol = + semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol + ?? semanticModel.GetDeclaredSymbol(selectedNode, cancellationToken); + + Assert.NotNull(selectedSymbol); + return selectedSymbol!; + } + + protected static string GetText(ValueTrackedItem item) + { + var sourceTree = item.Location.SourceTree; + var span = item.Location.SourceSpan; + + Assert.NotNull(sourceTree); + if (sourceTree!.TryGetText(out var text)) + { + return text!.GetSubText(span).ToString(); + } + + return sourceTree!.ToString(); + } + } +} \ No newline at end of file diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs index 57896af4cdfb4..4743e8c8e3687 100644 --- a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -15,51 +15,8 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking { [UseExportProvider] - public class CSharpValueTrackingTests + internal class CSharpValueTrackingTests : AbstractBaseValueTrackingTests { - private static async Task> GetTrackedItemsAsync(TestWorkspace testWorkspace, CancellationToken cancellationToken = default) - { - var cursorDocument = testWorkspace.DocumentWithCursor; - var document = testWorkspace.CurrentSolution.GetRequiredDocument(cursorDocument.Id); - var syntaxTree = await document.GetRequiredSyntaxTreeAsync(cancellationToken); - var textSpan = new TextSpan(cursorDocument.CursorPosition!.Value, 0); - var location = Location.Create(syntaxTree, textSpan); - var symbol = await GetSelectedSymbolAsync(textSpan, document, cancellationToken); - var service = testWorkspace.Services.GetRequiredService(); - return await service.TrackValueSourceAsync(testWorkspace.CurrentSolution, location, symbol, cancellationToken); - - } - - private static async Task GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) - { - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var selectedNode = root.FindNode(textSpan); - - Assert.NotNull(selectedNode); - - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var selectedSymbol = - semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol - ?? semanticModel.GetDeclaredSymbol(selectedNode, cancellationToken); - - Assert.NotNull(selectedSymbol); - return selectedSymbol!; - } - - private static string GetText(ValueTrackedItem item) - { - var sourceTree = item.Location.SourceTree; - var span = item.Location.SourceSpan; - - Assert.NotNull(sourceTree); - if (sourceTree!.TryGetText(out var text)) - { - return text!.GetSubText(span).ToString(); - } - - return sourceTree!.ToString(); - } - [Fact] public async Task TestProperty() { @@ -80,6 +37,11 @@ public void SetS(string s) using var workspace = TestWorkspace.CreateCSharp(code); var initialItems = await GetTrackedItemsAsync(workspace); + // + // property S + // |> S = s [Code.cs:8] + // |> public string S { get; set; } [Code.cs:4] + // Assert.Equal(2, initialItems.Length); Assert.Equal("S = s", GetText(initialItems[0])); Assert.Equal(@"public string S { get; set; } = """"", GetText(initialItems[1])); @@ -104,6 +66,11 @@ public void SetS(string s) using var workspace = TestWorkspace.CreateCSharp(code); var initialItems = await GetTrackedItemsAsync(workspace); + // + // field _s + // |> _s = s [Code.cs:8] + // |> _s = "" [Code.cs:4] + // Assert.Equal(2, initialItems.Length); Assert.Equal("_s = s", GetText(initialItems[0])); @@ -129,6 +96,11 @@ public int Add(int x, int y) using var workspace = TestWorkspace.CreateCSharp(code); var initialItems = await GetTrackedItemsAsync(workspace); + // + // local variable z + // |> z += y [Code.cs:7] + // |> var z = x [Code.cs:6] + // Assert.Equal(2, initialItems.Length); Assert.Equal(@"z += y", GetText(initialItems[0])); Assert.Equal("var z = x", GetText(initialItems[1])); @@ -150,6 +122,11 @@ public int Add(int $$x, int y) using var workspace = TestWorkspace.CreateCSharp(code); var initialItems = await GetTrackedItemsAsync(workspace); + // + // parameter x + // |> x += y [Code.cs:6] + // |> Add(int x, int y) [Code.cs:4] + // Assert.Equal(2, initialItems.Length); Assert.Equal(@"x += y", GetText(initialItems[0])); From 465dc8001bf5c7622091047aeef35e9312d0904e Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Tue, 16 Mar 2021 10:35:23 -0700 Subject: [PATCH 010/127] Fix test accessibility modifier --- .../Test/ValueTracking/AbstractBaseValueTrackingTests.cs | 8 ++++---- .../Test/ValueTracking/CSharpValueTrackingTests.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs index aaba47a3951c5..28332e481d739 100644 --- a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs @@ -14,9 +14,9 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking { - internal abstract class AbstractBaseValueTrackingTests + public abstract class AbstractBaseValueTrackingTests { - protected static async Task> GetTrackedItemsAsync(TestWorkspace testWorkspace, CancellationToken cancellationToken = default) + internal static async Task> GetTrackedItemsAsync(TestWorkspace testWorkspace, CancellationToken cancellationToken = default) { var cursorDocument = testWorkspace.DocumentWithCursor; var document = testWorkspace.CurrentSolution.GetRequiredDocument(cursorDocument.Id); @@ -29,7 +29,7 @@ protected static async Task> GetTrackedItemsAsy } - protected static async Task GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) + internal static async Task GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var selectedNode = root.FindNode(textSpan); @@ -45,7 +45,7 @@ protected static async Task GetSelectedSymbolAsync(TextSpan textSpan, D return selectedSymbol!; } - protected static string GetText(ValueTrackedItem item) + internal static string GetText(ValueTrackedItem item) { var sourceTree = item.Location.SourceTree; var span = item.Location.SourceSpan; diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs index 4743e8c8e3687..46045e4dd7b12 100644 --- a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking { [UseExportProvider] - internal class CSharpValueTrackingTests : AbstractBaseValueTrackingTests + public class CSharpValueTrackingTests : AbstractBaseValueTrackingTests { [Fact] public async Task TestProperty() From ec0ca3cfbb59be1a56d474f882f760db7418d608 Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Tue, 16 Mar 2021 12:47:47 -0700 Subject: [PATCH 011/127] WIP --- .../ValueTracking/ValueTrackingService.cs | 41 +----- .../ValueTracking/CSharpValueTrackingTests.cs | 12 +- .../VisualBasicValueTrackingTests.cs | 139 ++++++++++++++++++ 3 files changed, 147 insertions(+), 45 deletions(-) create mode 100644 src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 7f5f39dedb6cc..2b41e09697749 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -87,48 +87,19 @@ private static async Task> TrackAssignmentsAsync( foreach (var reference in referencesInDocument.Where(r => r.Location.IsWrittenTo)) { - AddAssignment(reference.Node, syntaxFacts); + var assignment = reference.Node.FirstAncestorOrSelf(syntaxFacts.IsLeftSideOfAnyAssignment); + if (assignment is not null && assignment.Parent is not null) + { + builder.Add(assignment.Parent.GetLocation()); + } } } } // Add all initializations of the symbol. Those are not caught in // the reference finder but should still show up in the tree - foreach (var location in symbol.Locations.Where(location => location.IsInSource)) - { - RoslynDebug.AssertNotNull(location.SourceTree); - - var node = location.FindNode(cancellationToken); - var document = solution.GetRequiredDocument(location.SourceTree); - var syntaxFacts = document.GetRequiredLanguageService(); - - AddAssignment(node, syntaxFacts); - } - + builder.AddRange(symbol.Locations.Where(location => location.IsInSource)); return builder.AsImmutableOrEmpty(); - - void AddAssignment(SyntaxNode node, ISyntaxFactsService syntaxFacts) - { - if (syntaxFacts.IsDeclaration(node) - || syntaxFacts.IsParameter(node)) - { - builder.Add(node.GetLocation()); - return; - } - - if (syntaxFacts.IsVariableDeclarator(node) && node.Parent is not null) - { - builder.Add(node.Parent.GetLocation()); - return; - } - - var assignment = node.FirstAncestorOrSelf(syntaxFacts.IsLeftSideOfAnyAssignment); - if (assignment is not null && assignment.Parent is not null) - { - builder.Add(assignment.Parent.GetLocation()); - return; - } - } } private static IReferenceFinder? GetReferenceFinder(ISymbol? symbol) diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs index 46045e4dd7b12..7ee12429b212b 100644 --- a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -2,13 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.ValueTracking; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; -using Microsoft.CodeAnalysis.Shared.Extensions; -using System.Threading; -using Microsoft.CodeAnalysis.Text; using Xunit; using Microsoft.CodeAnalysis.Test.Utilities; @@ -69,14 +64,11 @@ public void SetS(string s) // // field _s // |> _s = s [Code.cs:8] - // |> _s = "" [Code.cs:4] + // |> string _s = "" [Code.cs:4] // Assert.Equal(2, initialItems.Length); Assert.Equal("_s = s", GetText(initialItems[0])); - - // This is displaying the initializer but not the full line - // TODO: Fix this to be the whole line? - Assert.Equal(@"_s = """"", GetText(initialItems[1])); + Assert.Equal(@"string _s = """"", GetText(initialItems[1])); } [Fact] diff --git a/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs new file mode 100644 index 0000000000000..016f760e84063 --- /dev/null +++ b/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs @@ -0,0 +1,139 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking +{ + [UseExportProvider] + public class VisualBasicValueTrackingTests : AbstractBaseValueTrackingTests + { + [Fact] + public async Task TestProperty() + { + var code = +@" +Class C + Private _s As String + Public Property $$S() As String + Get + Return _s + End Get + Set(ByVal value As String) + _s = value + End Set + End Property + + + Public Sub SetS(s As String) + Me.S = s + End Sub + + Public Function GetS() As String + Return Me.S + End Function +End Class +"; + + using var workspace = TestWorkspace.CreateVisualBasic(code); + var initialItems = await GetTrackedItemsAsync(workspace); + + // + // property S + // |> Me.S = s [Code.vb:14] + // |> Public Property S() As String [Code.vb:4] + // + Assert.Equal(2, initialItems.Length); + Assert.Equal("Me.S = s", GetText(initialItems[0])); + Assert.Equal(@"Public Property S() As String", GetText(initialItems[1])); + } + + [Fact] + public async Task TestField() + { + var code = +@" +Class C + Private $$_s As String = """" + + Public Sub SetS(s As String) + Me._s = s + End Sub + + Public Function GetS() As String + Return Me._s + End Function +End Class +"; + + using var workspace = TestWorkspace.CreateVisualBasic(code); + var initialItems = await GetTrackedItemsAsync(workspace); + + // + // field _s + // |> Me._s = s [Code.vb:6] + // |> Private _s As String = "" [Code.vb:3] + // + Assert.Equal(2, initialItems.Length); + Assert.Equal("Me._s = s", GetText(initialItems[0])); + Assert.Equal(@"Private $$_s As String = """"", GetText(initialItems[1])); + } + + [Fact] + public async Task TestLocal() + { + var code = +@" +Class C + Public Function Add(x As Integer, y As Integer) As Integer + Dim $$z = x + z += y + Return z + End Function +End Class +"; + + using var workspace = TestWorkspace.CreateVisualBasic(code); + var initialItems = await GetTrackedItemsAsync(workspace); + + // + // local variable z + // |> z += y [Code.vb:5] + // |> Dim z = x [Code.vb:4] + // + Assert.Equal(2, initialItems.Length); + Assert.Equal("z += y", GetText(initialItems[0])); + Assert.Equal(@"Dim z = x", GetText(initialItems[1])); + } + + [Fact] + public async Task TestParameter() + { + var code = +@" +Class C + Public Function Add($$x As Integer, y As Integer) As Integer + x += y + Return x + End Function +End Class +"; + + using var workspace = TestWorkspace.CreateVisualBasic(code); + var initialItems = await GetTrackedItemsAsync(workspace); + + // + // parameter x + // |> x += y [Code.vb:4] + // |> Public Function Add(x As integer, y As Integer) As Integer [Code.vb:3] + // + Assert.Equal(2, initialItems.Length); + Assert.Equal("x += y", GetText(initialItems[0])); + Assert.Equal(@"Public Function Add(x As Integer, y As Integer) As Integer", GetText(initialItems[1])); + } + } +} From 87c0fa60cd912d68c92038d0d77896fd5bb0a95f Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Tue, 16 Mar 2021 13:46:39 -0700 Subject: [PATCH 012/127] Update tests to use line base. Use SymbolFinder --- .../ValueTracking/IValueTrackingService.cs | 5 +- .../ValueTrackingProgressCollector.cs | 36 ++++++ .../ValueTracking/ValueTrackingService.cs | 107 ++++++------------ .../AbstractBaseValueTrackingTests.cs | 20 +--- .../ValueTracking/CSharpValueTrackingTests.cs | 36 +++--- .../VisualBasicValueTrackingTests.cs | 30 ++--- 6 files changed, 114 insertions(+), 120 deletions(-) create mode 100644 src/EditorFeatures/Core/ValueTracking/ValueTrackingProgressCollector.cs diff --git a/src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs index 8504d153808f9..0097f80a96e5a 100644 --- a/src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs @@ -11,7 +11,10 @@ namespace Microsoft.CodeAnalysis.ValueTracking { internal interface IValueTrackingService : IWorkspaceService { - Task> TrackValueSourceAsync(Solution solution, Location location, ISymbol symbol, CancellationToken cancellationToken); + Task> TrackValueSourceAsync(Solution solution, ISymbol symbol, CancellationToken cancellationToken); + Task TrackValueSourceAsync(Solution solution, ISymbol symbol, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken); + Task> TrackValueSourceAsync(Solution solution, ValueTrackedItem previousTrackedItem, CancellationToken cancellationToken); + Task TrackValueSourceAsync(Solution solution, ValueTrackedItem previousTrackedItem, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken); } } diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingProgressCollector.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingProgressCollector.cs new file mode 100644 index 0000000000000..5be72a5feefb6 --- /dev/null +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingProgressCollector.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace Microsoft.CodeAnalysis.ValueTracking +{ + internal class ValueTrackingProgressCollector + { + private readonly object _lock = new(); + private readonly Queue _items = new(); + + public event EventHandler? OnNewItem; + + public void Add(ValueTrackedItem item) + { + lock (_lock) + { + _items.Enqueue(item); + } + + OnNewItem?.Invoke(null, item); + } + + public ImmutableArray GetItems() + { + lock (_lock) + { + return _items.ToImmutableArray(); + } + } + } +} diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 2b41e09697749..55f6e9b6a738e 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -3,21 +3,13 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; -using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindSymbols; -using Microsoft.CodeAnalysis.FindSymbols.Finders; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.LanguageServices; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ValueTracking { @@ -32,84 +24,59 @@ public ValueTrackingService() public async Task> TrackValueSourceAsync( Solution solution, - Location location, ISymbol symbol, CancellationToken cancellationToken) { - var referneceFinder = GetReferenceFinder(symbol); - if (referneceFinder is null) - { - return ImmutableArray.Empty; - } - - var assignments = await TrackAssignmentsAsync(solution, symbol, referneceFinder, cancellationToken).ConfigureAwait(false); - return assignments.Select(l => new ValueTrackedItem(l, symbol)).ToImmutableArray(); - } - - public Task> TrackValueSourceAsync( - Solution solution, - ValueTrackedItem previousTrackedItem, - CancellationToken cancellationToken) - { - throw new NotImplementedException(); + var progressTracker = new ValueTrackingProgressCollector(); + await TrackValueSourceAsync(solution, symbol, progressTracker, cancellationToken).ConfigureAwait(false); + return progressTracker.GetItems(); } - private static async Task> TrackAssignmentsAsync( + public async Task TrackValueSourceAsync( Solution solution, ISymbol symbol, - IReferenceFinder referenceFinder, + ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) { - using var _ = PooledObjects.ArrayBuilder.GetInstance(out var builder); - - // Add all the references to the symbol - // that write to it - var projectsToSearch = await referenceFinder.DetermineProjectsToSearchAsync(symbol, solution, solution.Projects.ToImmutableHashSet(), cancellationToken).ConfigureAwait(false); - foreach (var project in projectsToSearch) + if (symbol + is IPropertySymbol + or IFieldSymbol + or ILocalSymbol + or IParameterSymbol) { - var documentsToSearch = await referenceFinder.DetermineDocumentsToSearchAsync( - symbol, - project, - project.Documents.ToImmutableHashSet(), - FindReferencesSearchOptions.Default, - cancellationToken).ConfigureAwait(false); - - foreach (var document in documentsToSearch) + // Add all initializations of the symbol. Those are not caught in + // the reference finder but should still show up in the tree + foreach (var syntaxRef in symbol.DeclaringSyntaxReferences) { - var syntaxFacts = document.GetRequiredLanguageService(); - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var referencesInDocument = await referenceFinder.FindReferencesInDocumentAsync( - symbol, - document, - semanticModel, - FindReferencesSearchOptions.Default, - cancellationToken).ConfigureAwait(false); + var location = Location.Create(syntaxRef.SyntaxTree, syntaxRef.Span); + progressCollector.Add(new ValueTrackedItem(location, symbol)); + } - foreach (var reference in referencesInDocument.Where(r => r.Location.IsWrittenTo)) - { - var assignment = reference.Node.FirstAncestorOrSelf(syntaxFacts.IsLeftSideOfAnyAssignment); - if (assignment is not null && assignment.Parent is not null) - { - builder.Add(assignment.Parent.GetLocation()); - } - } + var references = await SymbolFinder.FindReferencesAsync(symbol, solution, cancellationToken).ConfigureAwait(false); + foreach (var refLocation in references.SelectMany(reference => reference.Locations.Where(l => l.IsWrittenTo))) + { + progressCollector.Add(new ValueTrackedItem(refLocation.Location, symbol)); } } + } - // Add all initializations of the symbol. Those are not caught in - // the reference finder but should still show up in the tree - builder.AddRange(symbol.Locations.Where(location => location.IsInSource)); - return builder.AsImmutableOrEmpty(); + public async Task> TrackValueSourceAsync( + Solution solution, + ValueTrackedItem previousTrackedItem, + CancellationToken cancellationToken) + { + var progressTracker = new ValueTrackingProgressCollector(); + await TrackValueSourceAsync(solution, previousTrackedItem, progressTracker, cancellationToken).ConfigureAwait(false); + return progressTracker.GetItems(); } - private static IReferenceFinder? GetReferenceFinder(ISymbol? symbol) - => symbol switch - { - IPropertySymbol => ReferenceFinders.Property, - IFieldSymbol => ReferenceFinders.Field, - ILocalSymbol => ReferenceFinders.Local, - IParameterSymbol => ReferenceFinders.Parameter, - _ => null - }; + public Task TrackValueSourceAsync( + Solution solution, + ValueTrackedItem previousTrackedItem, + ValueTrackingProgressCollector progressCollector, + CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } } } diff --git a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs index 28332e481d739..4fae961365ea8 100644 --- a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -25,7 +25,7 @@ internal static async Task> GetTrackedItemsAsyn var location = Location.Create(syntaxTree, textSpan); var symbol = await GetSelectedSymbolAsync(textSpan, document, cancellationToken); var service = testWorkspace.Services.GetRequiredService(); - return await service.TrackValueSourceAsync(testWorkspace.CurrentSolution, location, symbol, cancellationToken); + return await service.TrackValueSourceAsync(testWorkspace.CurrentSolution, symbol, cancellationToken); } @@ -45,18 +45,10 @@ internal static async Task GetSelectedSymbolAsync(TextSpan textSpan, Do return selectedSymbol!; } - internal static string GetText(ValueTrackedItem item) + internal static void ValidateItem(ValueTrackedItem item, int line) { - var sourceTree = item.Location.SourceTree; - var span = item.Location.SourceSpan; - - Assert.NotNull(sourceTree); - if (sourceTree!.TryGetText(out var text)) - { - return text!.GetSubText(span).ToString(); - } - - return sourceTree!.ToString(); + var lineSpan = item.Location.GetLineSpan(); + Assert.Equal(line, lineSpan.StartLinePosition.Line); } } -} \ No newline at end of file +} diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs index 7ee12429b212b..d55feb0426cea 100644 --- a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -34,12 +34,12 @@ public void SetS(string s) // // property S - // |> S = s [Code.cs:8] - // |> public string S { get; set; } [Code.cs:4] + // |> S = s [Code.cs:7] + // |> public string S { get; set; } [Code.cs:3] // Assert.Equal(2, initialItems.Length); - Assert.Equal("S = s", GetText(initialItems[0])); - Assert.Equal(@"public string S { get; set; } = """"", GetText(initialItems[1])); + ValidateItem(initialItems[0], 7); + ValidateItem(initialItems[1], 3); } [Fact] @@ -63,12 +63,12 @@ public void SetS(string s) // // field _s - // |> _s = s [Code.cs:8] - // |> string _s = "" [Code.cs:4] + // |> _s = s [Code.cs:7] + // |> string _s = "" [Code.cs:3] // Assert.Equal(2, initialItems.Length); - Assert.Equal("_s = s", GetText(initialItems[0])); - Assert.Equal(@"string _s = """"", GetText(initialItems[1])); + ValidateItem(initialItems[0], 7); + ValidateItem(initialItems[1], 3); } [Fact] @@ -90,12 +90,12 @@ public int Add(int x, int y) // // local variable z - // |> z += y [Code.cs:7] - // |> var z = x [Code.cs:6] + // |> z += y [Code.cs:6] + // |> var z = x [Code.cs:5] // Assert.Equal(2, initialItems.Length); - Assert.Equal(@"z += y", GetText(initialItems[0])); - Assert.Equal("var z = x", GetText(initialItems[1])); + ValidateItem(initialItems[0], 6); + ValidateItem(initialItems[1], 5); } [Fact] @@ -116,16 +116,12 @@ public int Add(int $$x, int y) // // parameter x - // |> x += y [Code.cs:6] - // |> Add(int x, int y) [Code.cs:4] + // |> x += y [Code.cs:5] + // |> Add(int x, int y) [Code.cs:3] // Assert.Equal(2, initialItems.Length); - Assert.Equal(@"x += y", GetText(initialItems[0])); - - // This is not whole line, but shows the full variable definition. - // Display may need to adjust for this, but the service is providing - // the correct information here - Assert.Equal("int x", GetText(initialItems[1])); + ValidateItem(initialItems[0], 5); + ValidateItem(initialItems[1], 3); } [Fact] diff --git a/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs index 016f760e84063..ed1536f33d809 100644 --- a/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs @@ -45,11 +45,11 @@ End Class // // property S // |> Me.S = s [Code.vb:14] - // |> Public Property S() As String [Code.vb:4] + // |> Public Property S() As String [Code.vb:3] // Assert.Equal(2, initialItems.Length); - Assert.Equal("Me.S = s", GetText(initialItems[0])); - Assert.Equal(@"Public Property S() As String", GetText(initialItems[1])); + ValidateItem(initialItems[0], 14); + ValidateItem(initialItems[1], 3); } [Fact] @@ -75,12 +75,12 @@ End Class // // field _s - // |> Me._s = s [Code.vb:6] - // |> Private _s As String = "" [Code.vb:3] + // |> Me._s = s [Code.vb:4] + // |> Private _s As String = "" [Code.vb:2] // Assert.Equal(2, initialItems.Length); - Assert.Equal("Me._s = s", GetText(initialItems[0])); - Assert.Equal(@"Private $$_s As String = """"", GetText(initialItems[1])); + ValidateItem(initialItems[0], 5); + ValidateItem(initialItems[1], 2); } [Fact] @@ -102,12 +102,12 @@ End Class // // local variable z - // |> z += y [Code.vb:5] - // |> Dim z = x [Code.vb:4] + // |> z += y [Code.vb:4] + // |> Dim z = x [Code.vb:3] // Assert.Equal(2, initialItems.Length); - Assert.Equal("z += y", GetText(initialItems[0])); - Assert.Equal(@"Dim z = x", GetText(initialItems[1])); + ValidateItem(initialItems[0], 4); + ValidateItem(initialItems[1], 3); } [Fact] @@ -128,12 +128,12 @@ End Class // // parameter x - // |> x += y [Code.vb:4] - // |> Public Function Add(x As integer, y As Integer) As Integer [Code.vb:3] + // |> x += y [Code.vb:3] + // |> Public Function Add(x As integer, y As Integer) As Integer [Code.vb:2] // Assert.Equal(2, initialItems.Length); - Assert.Equal("x += y", GetText(initialItems[0])); - Assert.Equal(@"Public Function Add(x As Integer, y As Integer) As Integer", GetText(initialItems[1])); + ValidateItem(initialItems[0], 3); + ValidateItem(initialItems[1], 2); } } } From ef3c027e3585e52742cd03eddfe983862488b487 Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Tue, 16 Mar 2021 14:03:17 -0700 Subject: [PATCH 013/127] Use ProgressTracking for SymbolFinder --- .../ValueTrackingProgressCollector.cs | 6 +-- .../ValueTracking/ValueTrackingService.cs | 49 ++++++++++++++++--- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingProgressCollector.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingProgressCollector.cs index 5be72a5feefb6..84a7d397657e4 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingProgressCollector.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingProgressCollector.cs @@ -11,15 +11,15 @@ namespace Microsoft.CodeAnalysis.ValueTracking internal class ValueTrackingProgressCollector { private readonly object _lock = new(); - private readonly Queue _items = new(); + private readonly Stack _items = new(); public event EventHandler? OnNewItem; - public void Add(ValueTrackedItem item) + public void Push(ValueTrackedItem item) { lock (_lock) { - _items.Enqueue(item); + _items.Push(item); } OnNewItem?.Invoke(null, item); diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 55f6e9b6a738e..194c0bd93bd5e 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -5,11 +5,11 @@ using System; using System.Collections.Immutable; using System.Composition; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.Utilities; namespace Microsoft.CodeAnalysis.ValueTracking { @@ -49,14 +49,13 @@ or ILocalSymbol foreach (var syntaxRef in symbol.DeclaringSyntaxReferences) { var location = Location.Create(syntaxRef.SyntaxTree, syntaxRef.Span); - progressCollector.Add(new ValueTrackedItem(location, symbol)); + progressCollector.Push(new ValueTrackedItem(location, symbol)); } - var references = await SymbolFinder.FindReferencesAsync(symbol, solution, cancellationToken).ConfigureAwait(false); - foreach (var refLocation in references.SelectMany(reference => reference.Locations.Where(l => l.IsWrittenTo))) - { - progressCollector.Add(new ValueTrackedItem(refLocation.Location, symbol)); - } + var findReferenceProgressCollector = new FindReferencesProgress(progressCollector); + await SymbolFinder.FindReferencesAsync( + symbol, solution, findReferenceProgressCollector, + documents: null, FindReferencesSearchOptions.Default, cancellationToken).ConfigureAwait(false); } } @@ -78,5 +77,41 @@ public Task TrackValueSourceAsync( { throw new NotImplementedException(); } + + private class FindReferencesProgress : IStreamingFindReferencesProgress, IStreamingProgressTracker + { + private readonly ValueTrackingProgressCollector _valueTrackingProgressCollector; + public FindReferencesProgress(ValueTrackingProgressCollector valueTrackingProgressCollector) + { + _valueTrackingProgressCollector = valueTrackingProgressCollector; + } + + public IStreamingProgressTracker ProgressTracker => this; + + public ValueTask AddItemsAsync(int count) => new(); + + public ValueTask ItemCompletedAsync() => new(); + + public ValueTask OnCompletedAsync() => new(); + + public ValueTask OnDefinitionFoundAsync(ISymbol symbol) => new(); + + public ValueTask OnFindInDocumentCompletedAsync(Document document) => new(); + + public ValueTask OnFindInDocumentStartedAsync(Document document) => new(); + + public ValueTask OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation location) + { + if (location.IsWrittenTo) + { + _valueTrackingProgressCollector.Push(new ValueTrackedItem(location.Location, symbol)); + } + + return new(); + } + + public ValueTask OnStartedAsync() => new(); + } + } } From 7df622b8c5cd60cb8a70d11441a126719549866f Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Thu, 18 Mar 2021 17:23:01 -0700 Subject: [PATCH 014/127] Update UI --- .../Core/ValueTracking/ValueTrackedItem.cs | 77 +++++++- .../ValueTracking/ValueTrackingService.cs | 20 +- .../AbstractBaseValueTrackingTests.cs | 3 +- src/VisualStudio/Core/Def/Commands.vsct | 2 +- .../Def/ValueTracking/BindableTextBlock.cs | 37 ++++ .../Def/ValueTracking/EmptyTreeViewItem.cs | 15 ++ .../Def/ValueTracking/TreeViewItemBase.cs | 43 +++++ .../ValueTrackedTreeItemViewModel.cs | 134 +++++++++++++ .../ValueTrackingCommandHandler.cs | 86 +++++++-- .../Def/ValueTracking/ValueTrackingTree.xaml | 49 +---- .../ValueTrackingTreeItemViewModel.cs | 179 +++++------------- .../ValueTrackingTreeRootViewModel.cs | 16 ++ .../ValueTrackingTreeViewModel.cs | 16 +- .../Core/Def/xlf/Commands.vsct.cs.xlf | 4 +- .../Core/Def/xlf/Commands.vsct.de.xlf | 4 +- .../Core/Def/xlf/Commands.vsct.es.xlf | 4 +- .../Core/Def/xlf/Commands.vsct.fr.xlf | 4 +- .../Core/Def/xlf/Commands.vsct.it.xlf | 4 +- .../Core/Def/xlf/Commands.vsct.ja.xlf | 4 +- .../Core/Def/xlf/Commands.vsct.ko.xlf | 4 +- .../Core/Def/xlf/Commands.vsct.pl.xlf | 4 +- .../Core/Def/xlf/Commands.vsct.pt-BR.xlf | 4 +- .../Core/Def/xlf/Commands.vsct.ru.xlf | 4 +- .../Core/Def/xlf/Commands.vsct.tr.xlf | 4 +- .../Core/Def/xlf/Commands.vsct.zh-Hans.xlf | 4 +- .../Core/Def/xlf/Commands.vsct.zh-Hant.xlf | 4 +- 26 files changed, 501 insertions(+), 228 deletions(-) create mode 100644 src/VisualStudio/Core/Def/ValueTracking/BindableTextBlock.cs create mode 100644 src/VisualStudio/Core/Def/ValueTracking/EmptyTreeViewItem.cs create mode 100644 src/VisualStudio/Core/Def/ValueTracking/TreeViewItemBase.cs create mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs create mode 100644 src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeRootViewModel.cs diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs index 0c8bd812e5b38..3fff8bac5048f 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs @@ -2,19 +2,86 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + namespace Microsoft.CodeAnalysis.ValueTracking { internal class ValueTrackedItem { - public Location Location { get; } public ISymbol Symbol { get; } + public ValueTrackedItem? Parent { get; } + + public Document Document { get; } + public TextSpan Span { get; } + public ImmutableArray ClassifiedSpans { get; } + public SourceText SourceText { get; } + public LineSpan LineSpan { get; } - public ValueTrackedItem( - Location location, - ISymbol symbol) + private ValueTrackedItem( + ISymbol symbol, + SourceText sourceText, + ImmutableArray classifiedSpans, + TextSpan textSpan, + Document document, + LineSpan lineSpan, + ValueTrackedItem? parent = null) { - Location = location; Symbol = symbol; + Parent = parent; + + Span = textSpan; + ClassifiedSpans = classifiedSpans; + SourceText = sourceText; + LineSpan = lineSpan; + Document = document; + } + + public static async Task TryCreateAsync(Solution solution, Location location, ISymbol symbol, ValueTrackedItem? parent = null, CancellationToken cancellationToken = default) + { + Contract.ThrowIfNull(location.SourceTree); + + var document = solution.GetRequiredDocument(location.SourceTree); + var excerptService = document.Services.GetService(); + SourceText? sourceText = null; + ImmutableArray classifiedSpans = default; + + if (excerptService != null) + { + var result = await excerptService.TryExcerptAsync(document, location.SourceSpan, ExcerptMode.SingleLine, cancellationToken).ConfigureAwait(false); + if (result.HasValue) + { + var value = result.Value; + sourceText = value.Content; + } + } + + if (sourceText is null) + { + var documentSpan = await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync(document, location.SourceSpan, cancellationToken).ConfigureAwait(false); + var classificationResult = await ClassifiedSpansAndHighlightSpanFactory.ClassifyAsync(documentSpan, cancellationToken).ConfigureAwait(false); + classifiedSpans = classificationResult.ClassifiedSpans; + sourceText = await location.SourceTree.GetTextAsync(cancellationToken).ConfigureAwait(false); + } + + sourceText.GetLineAndOffset(location.SourceSpan.Start, out var lineStart, out var _); + sourceText.GetLineAndOffset(location.SourceSpan.End, out var lineEnd, out var _); + var lineSpan = LineSpan.FromBounds(lineStart, lineEnd); + + return new ValueTrackedItem( + symbol, + sourceText, + classifiedSpans, + location.SourceSpan, + document, + lineSpan, + parent: parent); } } } diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 194c0bd93bd5e..69d9bc92a6cfa 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -49,7 +49,11 @@ or ILocalSymbol foreach (var syntaxRef in symbol.DeclaringSyntaxReferences) { var location = Location.Create(syntaxRef.SyntaxTree, syntaxRef.Span); - progressCollector.Push(new ValueTrackedItem(location, symbol)); + var item = await ValueTrackedItem.TryCreateAsync(solution, location, symbol, parent: null, cancellationToken).ConfigureAwait(false); + if (item is not null) + { + progressCollector.Push(item); + } } var findReferenceProgressCollector = new FindReferencesProgress(progressCollector); @@ -75,7 +79,7 @@ public Task TrackValueSourceAsync( ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) { - throw new NotImplementedException(); + return Task.CompletedTask; } private class FindReferencesProgress : IStreamingFindReferencesProgress, IStreamingProgressTracker @@ -100,18 +104,20 @@ public FindReferencesProgress(ValueTrackingProgressCollector valueTrackingProgre public ValueTask OnFindInDocumentStartedAsync(Document document) => new(); - public ValueTask OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation location) + public async ValueTask OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation location) { if (location.IsWrittenTo) { - _valueTrackingProgressCollector.Push(new ValueTrackedItem(location.Location, symbol)); + var solution = location.Document.Project.Solution; + var item = await ValueTrackedItem.TryCreateAsync(solution, location.Location, symbol, parent: null, CancellationToken.None).ConfigureAwait(false); + if (item is not null) + { + _valueTrackingProgressCollector.Push(item); + } } - - return new(); } public ValueTask OnStartedAsync() => new(); } - } } diff --git a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs index 4fae961365ea8..946464e4a8398 100644 --- a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs @@ -47,8 +47,7 @@ internal static async Task GetSelectedSymbolAsync(TextSpan textSpan, Do internal static void ValidateItem(ValueTrackedItem item, int line) { - var lineSpan = item.Location.GetLineSpan(); - Assert.Equal(line, lineSpan.StartLinePosition.Line); + Assert.Equal(line, item.LineSpan.Start); } } } diff --git a/src/VisualStudio/Core/Def/Commands.vsct b/src/VisualStudio/Core/Def/Commands.vsct index 591fab85c9ba0..ae1d78ad48c01 100644 --- a/src/VisualStudio/Core/Def/Commands.vsct +++ b/src/VisualStudio/Core/Def/Commands.vsct @@ -418,7 +418,7 @@ DefaultInvisible CommandWellOnly - Show Value Tracking + Track Value Source ShowValueTrackingCommandName ShowValueTracking ViewEditorConfigSettings diff --git a/src/VisualStudio/Core/Def/ValueTracking/BindableTextBlock.cs b/src/VisualStudio/Core/Def/ValueTracking/BindableTextBlock.cs new file mode 100644 index 0000000000000..37f1696f34b32 --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/BindableTextBlock.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using Roslyn.Utilities; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class BindableTextBlock : TextBlock + { + public IList InlineCollection + { + get { return (ObservableCollection)GetValue(InlineListProperty); } + set { SetValue(InlineListProperty, value); } + } + + public static readonly DependencyProperty InlineListProperty = + DependencyProperty.Register(nameof(InlineCollection), typeof(IList), typeof(BindableTextBlock), new UIPropertyMetadata(null, OnPropertyChanged)); + + private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) + { + var textBlock = (BindableTextBlock)sender; + var newList = (IList)e.NewValue; + + textBlock.Inlines.Clear(); + foreach (var inline in newList) + { + textBlock.Inlines.Add(inline); + } + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/EmptyTreeViewItem.cs b/src/VisualStudio/Core/Def/ValueTracking/EmptyTreeViewItem.cs new file mode 100644 index 0000000000000..767ecab7af32f --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/EmptyTreeViewItem.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class EmptyTreeViewItem : TreeViewItemBase + { + public static EmptyTreeViewItem Instance { get; } = new(); + + private EmptyTreeViewItem() + { + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/TreeViewItemBase.cs b/src/VisualStudio/Core/Def/ValueTracking/TreeViewItemBase.cs new file mode 100644 index 0000000000000..d7cb529bc40e1 --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/TreeViewItemBase.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class TreeViewItemBase : INotifyPropertyChanged + { + private bool _isExpanded = false; + public bool IsNodeExpanded + { + get => _isExpanded; + set => SetProperty(ref _isExpanded, value); + } + + private bool _isSelected = false; + public bool IsNodeSelected + { + get => _isSelected; + set => SetProperty(ref _isSelected, value); + } + + public event PropertyChangedEventHandler? PropertyChanged; + + protected void SetProperty(ref T field, T value, [CallerMemberName] string name = "") + { + if (EqualityComparer.Default.Equals(field, value)) + { + return; + } + + field = value; + NotifyPropertyChanged(name); + } + + protected void NotifyPropertyChanged(string name) + => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs new file mode 100644 index 0000000000000..a286cc588bfe2 --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs @@ -0,0 +1,134 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.ValueTracking; +using Roslyn.Utilities; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Language.Intellisense; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class ValueTrackedTreeItemViewModel : ValueTrackingTreeItemViewModel + { + private bool _childrenCalculated; + private readonly Solution _solution; + private readonly IClassificationFormatMap _classificationFormatMap; + private readonly ClassificationTypeMap _classificationTypeMap; + private readonly IGlyphService _glyphService; + private readonly IValueTrackingService _valueTrackingService; + private readonly ValueTrackedItem _trackedItem; + + + public ValueTrackedTreeItemViewModel( + ValueTrackedItem trackedItem, + Solution solution, + IClassificationFormatMap classificationFormatMap, + ClassificationTypeMap classificationTypeMap, + IGlyphService glyphService, + IValueTrackingService valueTrackingService, + ImmutableArray children = default) + : base( + trackedItem.Document, + trackedItem.LineSpan.Start, + trackedItem.SourceText, + trackedItem.Symbol, + trackedItem.ClassifiedSpans, + classificationFormatMap, + classificationTypeMap, + glyphService) + { + + _trackedItem = trackedItem; + _solution = solution; + _classificationFormatMap = classificationFormatMap; + _classificationTypeMap = classificationTypeMap; + _glyphService = glyphService; + _valueTrackingService = valueTrackingService; + + if (children.IsDefaultOrEmpty) + { + // Add an empty item so the treeview has an expansion showing to calculate + // the actual children of the node + ChildItems.Add(EmptyTreeViewItem.Instance); + + ChildItems.CollectionChanged += (s, a) => + { + NotifyPropertyChanged(nameof(ChildItems)); + }; + + PropertyChanged += (s, a) => + { + if (a.PropertyName == nameof(IsNodeExpanded)) + { + CalculateChildren(); + } + }; + } + } + + private void CalculateChildren() + { + ThreadHelper.ThrowIfNotOnUIThread(); + + if (_childrenCalculated) + { + return; + } + + _childrenCalculated = true; + + CalculateChildrenAsync(CancellationToken.None) + .ContinueWith(task => + { + if (task.Exception is not null) + { + _childrenCalculated = false; + return; + } + + ChildItems.Clear(); + + foreach (var item in task.Result) + { + ChildItems.Add(item); + } + }, + + // Use the UI thread synchronization context for calling back to the UI + // thread to add the tiems + TaskScheduler.FromCurrentSynchronizationContext()); + } + + private async Task> CalculateChildrenAsync(CancellationToken cancellationToken) + { + var valueTrackedItems = await _valueTrackingService.TrackValueSourceAsync( + _solution, + _trackedItem, + cancellationToken).ConfigureAwait(false); + + // TODO: Use pooled item here? + var builder = new List(); + foreach (var valueTrackedItem in valueTrackedItems) + { + builder.Add(new ValueTrackedTreeItemViewModel( + valueTrackedItem, + _solution, + _classificationFormatMap, + _classificationTypeMap, + _glyphService, + _valueTrackingService)); + } + + return builder.ToImmutableArray(); + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs index 2bcbb7f1463eb..22e82f900950d 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs @@ -3,11 +3,13 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Immutable; using System.ComponentModel.Composition; using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; @@ -18,11 +20,15 @@ using Microsoft.CodeAnalysis.ValueTracking; using Microsoft.VisualStudio.Commanding; using Microsoft.VisualStudio.GraphModel.CodeSchema; +using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.LanguageServices.Setup; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Utilities; +using Roslyn.Utilities; using IAsyncServiceProvider = Microsoft.VisualStudio.Shell.IAsyncServiceProvider; using Task = System.Threading.Tasks.Task; @@ -34,16 +40,28 @@ namespace Microsoft.VisualStudio.LanguageServices.ValueTracking internal class ValueTrackingCommandHandler : ICommandHandler { private readonly IAsyncServiceProvider _serviceProvider; + private readonly SVsServiceProvider _serviceProvider1; private readonly IThreadingContext _threadingContext; - + private readonly ClassificationTypeMap _typeMap; + private readonly IClassificationFormatMapService _classificationFormatMapService; + private readonly IGlyphService _glyphService; private RoslynPackage? _roslynPackage; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public ValueTrackingCommandHandler(SVsServiceProvider serviceProvider, IThreadingContext threadingContext) + public ValueTrackingCommandHandler( + SVsServiceProvider serviceProvider, + IThreadingContext threadingContext, + ClassificationTypeMap typeMap, + IClassificationFormatMapService classificationFormatMapService, + IGlyphService glyphService) { _serviceProvider = (IAsyncServiceProvider)serviceProvider; + _serviceProvider1 = serviceProvider; _threadingContext = threadingContext; + _typeMap = typeMap; + _classificationFormatMapService = classificationFormatMapService; + _glyphService = glyphService; } public string DisplayName => "Go to value tracking"; @@ -77,7 +95,7 @@ public bool ExecuteCommand(ValueTrackingEditorCommandArgs args, CommandExecution var syntaxTree = document.GetRequiredSyntaxTreeSynchronously(cancellationToken); var location = Location.Create(syntaxTree, textSpan); - _threadingContext.JoinableTaskFactory.Run(() => ShowToolWindowAsync(selectedSymbol, location, document.Project.Solution, cancellationToken)); + _threadingContext.JoinableTaskFactory.Run(() => ShowToolWindowAsync(args.TextView, selectedSymbol, location, document.Project.Solution, cancellationToken)); return true; } @@ -114,25 +132,70 @@ or IParameterSymbol }; } - private async Task ShowToolWindowAsync(ISymbol selectedSymbol, Location location, Solution solution, CancellationToken cancellationToken) + private async Task ShowToolWindowAsync(ITextView textView, ISymbol selectedSymbol, Location location, Solution solution, CancellationToken cancellationToken) { - var roslynPackage = await TryGetRoslynPackageAsync(cancellationToken).ConfigureAwait(false); + var item = await ValueTrackedItem.TryCreateAsync(solution, location, selectedSymbol, cancellationToken: cancellationToken).ConfigureAwait(false); + if (item is null) + { + return; + } + + var valueTrackingService = solution.Workspace.Services.GetRequiredService(); + var classificationFormatMap = _classificationFormatMapService.GetClassificationFormatMap(textView); + + var childItems = await valueTrackingService.TrackValueSourceAsync(solution, item, cancellationToken).ConfigureAwait(false); + var childViewModels = childItems.SelectAsArray(child => CreateViewModel(child)); + + RoslynDebug.AssertNotNull(location.SourceTree); + var document = solution.GetRequiredDocument(location.SourceTree); + + var sourceText = await location.SourceTree.GetTextAsync(cancellationToken).ConfigureAwait(false); + sourceText.GetLineAndOffset(location.SourceSpan.Start, out var lineStart, out var _); + sourceText.GetLineAndOffset(location.SourceSpan.End, out var lineEnd, out var _); + var lineSpan = LineSpan.FromBounds(lineStart, lineEnd); + + var documentSpan = await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync(document, location.SourceSpan, cancellationToken).ConfigureAwait(false); + var classificationResult = await ClassifiedSpansAndHighlightSpanFactory.ClassifyAsync(documentSpan, cancellationToken).ConfigureAwait(false); + var root = new ValueTrackingTreeItemViewModel( + document, + lineSpan.Start, + sourceText, + selectedSymbol, + classificationResult.ClassifiedSpans, + classificationFormatMap, + _typeMap, + _glyphService, + childViewModels); + + await ShowToolWindowAsync(root, cancellationToken).ConfigureAwait(false); + + ValueTrackingTreeItemViewModel CreateViewModel(ValueTrackedItem valueTrackedItem, ImmutableArray children = default) + => new ValueTrackedTreeItemViewModel( + valueTrackedItem, + solution, + classificationFormatMap, + _typeMap, + _glyphService, + valueTrackingService, + children); + } + + private async Task ShowToolWindowAsync(ValueTrackingTreeItemViewModel viewModel, CancellationToken cancellationToken) + { + var roslynPackage = await TryGetRoslynPackageAsync(cancellationToken).ConfigureAwait(false); if (roslynPackage is null) { return; } - var dataFlowItem = new ValueTrackingTreeItemViewModel( - new ValueTrackedItem(location, selectedSymbol), - solution, - solution.Workspace.Services.GetRequiredService()); + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); if (ValueTrackingToolWindow.Instance is null) { var factory = roslynPackage.GetAsyncToolWindowFactory(Guids.ValueTrackingToolWindowId); - factory.CreateToolWindow(Guids.ValueTrackingToolWindowId, 0, dataFlowItem); + factory.CreateToolWindow(Guids.ValueTrackingToolWindowId, 0, viewModel); await factory.InitializeToolWindowAsync(Guids.ValueTrackingToolWindowId, 0); ValueTrackingToolWindow.Instance = (ValueTrackingToolWindow)await roslynPackage.ShowToolWindowAsync( @@ -143,7 +206,8 @@ private async Task ShowToolWindowAsync(ISymbol selectedSymbol, Location location } else { - ValueTrackingToolWindow.Instance.Root = dataFlowItem; + ValueTrackingToolWindow.Instance.Root = viewModel; + await roslynPackage.ShowToolWindowAsync( typeof(ValueTrackingToolWindow), 0, diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml index 2b19c1bce37bd..c84201404c116 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml @@ -8,7 +8,7 @@ mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs index a82eb4936d020..c212d264d9d15 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs @@ -2,162 +2,83 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.IO; using System.Collections.Generic; -using System.Collections.Immutable; using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.ValueTracking; -using Roslyn.Utilities; +using System.Windows.Documents; +using System.Windows.Media; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.VisualStudio.Language.Intellisense; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Editor.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; namespace Microsoft.VisualStudio.LanguageServices.ValueTracking { - internal class TreeViewItemBase : INotifyPropertyChanged + internal class ValueTrackingTreeItemViewModel : TreeViewItemBase { - private bool _isExpanded = false; - public bool IsNodeExpanded - { - get => _isExpanded; - set => SetProperty(ref _isExpanded, value); - } + private readonly SourceText _sourceText; + private readonly ISymbol _symbol; + private readonly IClassificationFormatMap _classificationFormatMap; + private readonly ClassificationTypeMap _classificationTypeMap; + private readonly IGlyphService _glyphService; - private bool _isSelected = false; - public bool IsNodeSelected - { - get => _isSelected; - set => SetProperty(ref _isSelected, value); - } - - public event PropertyChangedEventHandler? PropertyChanged; + protected Document Document { get; } + protected int LineNumber { get; } - protected void SetProperty(ref T field, T value, [CallerMemberName] string name = "") - { - if (EqualityComparer.Default.Equals(field, value)) - { - return; - } - - field = value; - NotifyPropertyChanged(name); - } - - protected void NotifyPropertyChanged(string name) - => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); - } - internal class EmptyTreeViewItem : TreeViewItemBase - { - public static EmptyTreeViewItem Instance { get; } = new(); - - private EmptyTreeViewItem() - { - } - } + public ObservableCollection ChildItems { get; } = new(); - internal class ValueTrackingTreeItemViewModel : TreeViewItemBase - { - private bool _childrenCalculated; - private readonly Solution _solution; - private readonly IValueTrackingService _valueTrackingService; + public string FileDisplay => $"[{Document.Name}:{LineNumber}]"; - public ObservableCollection ChildItems { get; } = new(); + public ImageSource GlyphImage => _symbol.GetGlyph().GetImageSource(_glyphService); - public Location Location => TrackedItem.Location; - public ISymbol Symbol => TrackedItem.Symbol; + public ImmutableArray ClassifiedSpans { get; } - public string FileName => Path.GetFileName(Location.SourceTree!.FilePath); - public string LineDisplay + public IList Inlines { get { - var linePosition = Location.GetLineSpan().StartLinePosition; - return linePosition.Line.ToString(); + var classifiedTexts = ClassifiedSpans.SelectAsArray( + cs => new ClassifiedText(cs.ClassificationType, _sourceText.ToString(cs.TextSpan))); + + return classifiedTexts.ToInlines( + _classificationFormatMap, + _classificationTypeMap); } } - public string SymbolName => Symbol.ToDisplayString(); - public string ContainerName => Symbol.ContainingSymbol.ToDisplayString(); - - public ValueTrackedItem TrackedItem { get; } - public ValueTrackingTreeItemViewModel( - ValueTrackedItem trackedItem, - Solution solution, - IValueTrackingService valueTrackingService) + Document document, + int lineNumber, + SourceText sourceText, + ISymbol symbol, + ImmutableArray classifiedSpans, + IClassificationFormatMap classificationFormatMap, + ClassificationTypeMap classificationTypeMap, + IGlyphService glyphService, + ImmutableArray children = default) { - Contract.ThrowIfFalse(trackedItem.Location.IsInSource); + Document = document; + LineNumber = lineNumber; + ClassifiedSpans = classifiedSpans; - ChildItems.Add(EmptyTreeViewItem.Instance); + _sourceText = sourceText; + _symbol = symbol; + _classificationFormatMap = classificationFormatMap; + _classificationTypeMap = classificationTypeMap; + _glyphService = glyphService; - TrackedItem = trackedItem; - _solution = solution; - _valueTrackingService = valueTrackingService; - - ChildItems.CollectionChanged += (s, a) => - { - NotifyPropertyChanged(nameof(ChildItems)); - }; - - PropertyChanged += (s, a) => + if (!children.IsDefaultOrEmpty) { - if (a.PropertyName == nameof(IsNodeExpanded)) + foreach (var child in children) { - if (_childrenCalculated) - { - return; - } - - _childrenCalculated = true; - - // TODO: What cancellationtoken to use here? - CalculateChildrenAsync(CancellationToken.None) - .ContinueWith(task => - { - if (task.Exception is not null) - { - _childrenCalculated = false; - return; - } - - ChildItems.Clear(); - - foreach (var item in task.Result) - { - ChildItems.Add(item); - } - }, - - // Use the UI thread synchronization context for calling back to the UI - // thread to add the tiems - TaskScheduler.FromCurrentSynchronizationContext()); - + ChildItems.Add(child); } - }; - } - - private async Task> CalculateChildrenAsync(CancellationToken cancellationToken) - { - var valueTrackedItems = await _valueTrackingService.TrackValueSourceAsync( - _solution, - TrackedItem, - cancellationToken).ConfigureAwait(false); - - // TODO: Use pooled item here? - var builder = new List(); - foreach (var valueTrackedItem in valueTrackedItems) - { - builder.Add(new ValueTrackingTreeItemViewModel( - valueTrackedItem, - _solution, - _valueTrackingService)); } - - return builder.ToImmutableArray(); } } } diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeRootViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeRootViewModel.cs new file mode 100644 index 0000000000000..7617daec33d9d --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeRootViewModel.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class ValueTrackingTreeRootViewModel : TreeViewItemBase + { + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs index 486bb1ec71c90..aad24f697ff9a 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs @@ -11,11 +11,18 @@ namespace Microsoft.VisualStudio.LanguageServices.ValueTracking { internal class ValueTrackingTreeViewModel : INotifyPropertyChanged { - public ValueTrackingTreeViewModel(ValueTrackingTreeItemViewModel root) + public ValueTrackingTreeViewModel(params ValueTrackingTreeItemViewModel[] roots) { - Roots.Add(root); + foreach (var root in roots) + { + Roots.Add(root); + } } + public ValueTrackingTreeViewModel() + : this(GetSample()) + { } + public ObservableCollection Roots { get; } = new(); public event PropertyChangedEventHandler? PropertyChanged; @@ -33,5 +40,10 @@ private void SetProperty(ref T field, T value, [CallerMemberName] string name private void NotifyPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + + public static ValueTrackingTreeItemViewModel[] GetSample() + { + return new ValueTrackingTreeItemViewModel[0]; + } } } diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf index 2e5317d02b9e6..24399de7b3f2d 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf @@ -423,8 +423,8 @@ - Show Value Tracking - Show Value Tracking + Track Value Source + Track Value Source diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf index 4027a95cb4251..cfa0d67550f38 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf @@ -423,8 +423,8 @@ - Show Value Tracking - Show Value Tracking + Track Value Source + Track Value Source diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf index 927958975d1ed..27ce157a48fe2 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf @@ -423,8 +423,8 @@ - Show Value Tracking - Show Value Tracking + Track Value Source + Track Value Source diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf index a7b29c442ae2c..5ebbababcaa00 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf @@ -423,8 +423,8 @@ - Show Value Tracking - Show Value Tracking + Track Value Source + Track Value Source diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf index d0f71b5ac9223..3a18f2f4296e8 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf @@ -423,8 +423,8 @@ - Show Value Tracking - Show Value Tracking + Track Value Source + Track Value Source diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf index ad582208aea51..40f558657176d 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf @@ -423,8 +423,8 @@ - Show Value Tracking - Show Value Tracking + Track Value Source + Track Value Source diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf index bd4c80e62708c..391f41638f42c 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf @@ -423,8 +423,8 @@ - Show Value Tracking - Show Value Tracking + Track Value Source + Track Value Source diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf index 6eeca8d5d5a35..8dcb1fd6e47aa 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf @@ -423,8 +423,8 @@ - Show Value Tracking - Show Value Tracking + Track Value Source + Track Value Source diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf index fdc3219090701..6183276ec3987 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf @@ -423,8 +423,8 @@ - Show Value Tracking - Show Value Tracking + Track Value Source + Track Value Source diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf index ec8809be173ed..aa6728418b0a7 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf @@ -423,8 +423,8 @@ - Show Value Tracking - Show Value Tracking + Track Value Source + Track Value Source diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf index 4190512bc5068..6357ff5443c7e 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf @@ -423,8 +423,8 @@ - Show Value Tracking - Show Value Tracking + Track Value Source + Track Value Source diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf index 984d36ecf7968..4b191897c05c1 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf @@ -423,8 +423,8 @@ - Show Value Tracking - Show Value Tracking + Track Value Source + Track Value Source diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf index 223c86fd530dc..a4a5dfb4ea579 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf @@ -423,8 +423,8 @@ - Show Value Tracking - Show Value Tracking + Track Value Source + Track Value Source From 86ba825456d239b7ac9d4aecc498bf7a39c229ca Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Thu, 18 Mar 2021 17:25:59 -0700 Subject: [PATCH 015/127] Fix correctness build --- src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 194c0bd93bd5e..5cb4ed66a3e6d 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -112,6 +112,5 @@ public ValueTask OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation locatio public ValueTask OnStartedAsync() => new(); } - } } From cdfb8dc70225946ba61101a57b57a6250311d227 Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Thu, 18 Mar 2021 19:33:42 -0700 Subject: [PATCH 016/127] PR feedback --- .../Core/ValueTracking/ValueTrackingProgressCollector.cs | 4 ++-- src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs | 4 ++-- .../Test/ValueTracking/AbstractBaseValueTrackingTests.cs | 3 --- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingProgressCollector.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingProgressCollector.cs index 84a7d397657e4..b4061f4ba1fe7 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingProgressCollector.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingProgressCollector.cs @@ -8,14 +8,14 @@ namespace Microsoft.CodeAnalysis.ValueTracking { - internal class ValueTrackingProgressCollector + internal class ValueTrackingProgressCollector : IProgress { private readonly object _lock = new(); private readonly Stack _items = new(); public event EventHandler? OnNewItem; - public void Push(ValueTrackedItem item) + public void Report(ValueTrackedItem item) { lock (_lock) { diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 5cb4ed66a3e6d..95c39d8d344bd 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -49,7 +49,7 @@ or ILocalSymbol foreach (var syntaxRef in symbol.DeclaringSyntaxReferences) { var location = Location.Create(syntaxRef.SyntaxTree, syntaxRef.Span); - progressCollector.Push(new ValueTrackedItem(location, symbol)); + progressCollector.Report(new ValueTrackedItem(location, symbol)); } var findReferenceProgressCollector = new FindReferencesProgress(progressCollector); @@ -104,7 +104,7 @@ public ValueTask OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation locatio { if (location.IsWrittenTo) { - _valueTrackingProgressCollector.Push(new ValueTrackedItem(location.Location, symbol)); + _valueTrackingProgressCollector.Report(new ValueTrackedItem(location.Location, symbol)); } return new(); diff --git a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs index 4fae961365ea8..40d4d8d82a33a 100644 --- a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs @@ -10,7 +10,6 @@ using System.Threading; using Microsoft.CodeAnalysis.Text; using Xunit; -using Microsoft.CodeAnalysis.Test.Utilities; namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking { @@ -20,9 +19,7 @@ internal static async Task> GetTrackedItemsAsyn { var cursorDocument = testWorkspace.DocumentWithCursor; var document = testWorkspace.CurrentSolution.GetRequiredDocument(cursorDocument.Id); - var syntaxTree = await document.GetRequiredSyntaxTreeAsync(cancellationToken); var textSpan = new TextSpan(cursorDocument.CursorPosition!.Value, 0); - var location = Location.Create(syntaxTree, textSpan); var symbol = await GetSelectedSymbolAsync(textSpan, document, cancellationToken); var service = testWorkspace.Services.GetRequiredService(); return await service.TrackValueSourceAsync(testWorkspace.CurrentSolution, symbol, cancellationToken); From c136795f7a0864e82f01a38c27da7eec8935fbb7 Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Fri, 19 Mar 2021 10:44:20 -0700 Subject: [PATCH 017/127] Make async instead of blocking UI --- .../ValueTracking/ValueTrackingService.cs | 8 ++----- .../ValueTrackingCommandHandler.cs | 18 +++++++++------ .../ValueTracking/ValueTrackingToolWindow.cs | 22 +++---------------- 3 files changed, 16 insertions(+), 32 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index a9720567b904c..9effa93548c32 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -49,15 +49,11 @@ or ILocalSymbol foreach (var syntaxRef in symbol.DeclaringSyntaxReferences) { var location = Location.Create(syntaxRef.SyntaxTree, syntaxRef.Span); -<<<<<<< HEAD var item = await ValueTrackedItem.TryCreateAsync(solution, location, symbol, parent: null, cancellationToken).ConfigureAwait(false); if (item is not null) { - progressCollector.Push(item); + progressCollector.Report(item); } -======= - progressCollector.Report(new ValueTrackedItem(location, symbol)); ->>>>>>> upstream/features/value_tracking } var findReferenceProgressCollector = new FindReferencesProgress(progressCollector); @@ -116,7 +112,7 @@ public async ValueTask OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation l var item = await ValueTrackedItem.TryCreateAsync(solution, location.Location, symbol, parent: null, CancellationToken.None).ConfigureAwait(false); if (item is not null) { - _valueTrackingProgressCollector.Report(new ValueTrackedItem(location.Location, symbol)); + _valueTrackingProgressCollector.Report(item); } } } diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs index 22e82f900950d..434617c890be0 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs @@ -86,16 +86,20 @@ public bool ExecuteCommand(ValueTrackingEditorCommandArgs args, CommandExecution return false; } - var selectedSymbol = _threadingContext.JoinableTaskFactory.Run(() => GetSelectedSymbolAsync(textSpan, document, cancellationToken)); - if (selectedSymbol is null) + _threadingContext.JoinableTaskFactory.RunAsync(async () => { - return false; - } + var selectedSymbol = await GetSelectedSymbolAsync(textSpan, document, cancellationToken).ConfigureAwait(false); + if (selectedSymbol is null) + { + // TODO: Show error dialog + return; + } - var syntaxTree = document.GetRequiredSyntaxTreeSynchronously(cancellationToken); - var location = Location.Create(syntaxTree, textSpan); + var syntaxTree = document.GetRequiredSyntaxTreeSynchronously(cancellationToken); + var location = Location.Create(syntaxTree, textSpan); - _threadingContext.JoinableTaskFactory.Run(() => ShowToolWindowAsync(args.TextView, selectedSymbol, location, document.Project.Solution, cancellationToken)); + await ShowToolWindowAsync(args.TextView, selectedSymbol, location, document.Project.Solution, cancellationToken).ConfigureAwait(false); + }); return true; } diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs index 67c24e9cf3d5e..bfbad0894d99a 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs @@ -3,15 +3,8 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.Linq; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Windows; -using System.Windows.Controls; -using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.Shell; namespace Microsoft.VisualStudio.LanguageServices.ValueTracking @@ -22,9 +15,7 @@ internal class ValueTrackingToolWindow : ToolWindowPane public static ValueTrackingToolWindow? Instance { get; set; } private readonly ValueTrackingTreeViewModel _viewModel; - // Needed for VSSDK003 - // See https://github.com/Microsoft/VSSDK-Analyzers/blob/main/doc/VSSDK003.md - public ValueTrackingToolWindow(object o) + public ValueTrackingToolWindow(ValueTrackingTreeItemViewModel root) : base(null) { if (Instance is not null) @@ -34,15 +25,8 @@ public ValueTrackingToolWindow(object o) this.Caption = "Value Tracking"; - if (o is ValueTrackingTreeItemViewModel root) - { - _viewModel = new ValueTrackingTreeViewModel(root); - Content = new ValueTrackingTree(_viewModel); - } - else - { - throw new Exception("This shouldn't happen"); - } + _viewModel = new ValueTrackingTreeViewModel(root); + Content = new ValueTrackingTree(_viewModel); } public ValueTrackingTreeItemViewModel Root From 56b09014316d8f2dd03b3e6f78d38679eada4798 Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Fri, 19 Mar 2021 10:56:20 -0700 Subject: [PATCH 018/127] Undo csproj changes --- .../Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj | 1 - .../Core/Portable/Microsoft.CodeAnalysis.Features.csproj | 3 --- .../Core/Def/Microsoft.VisualStudio.LanguageServices.csproj | 5 +++-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj b/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj index 19a867ba77c57..4211ba99baff2 100644 --- a/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj +++ b/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj @@ -69,7 +69,6 @@ - diff --git a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj index 329ab439a18f7..bf59d330743a2 100644 --- a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj +++ b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj @@ -119,9 +119,6 @@ - - - diff --git a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj index 1eb93e319b038..5fab552e80282 100644 --- a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj +++ b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj @@ -33,7 +33,7 @@ 6cf2e545-6109-4730-8883-cf43d7aec3e1 - + @@ -199,7 +199,8 @@ - + From d7fcd43a3399bf51e8da0ffdf882ea4395fb3d02 Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Fri, 19 Mar 2021 12:53:25 -0700 Subject: [PATCH 019/127] Fix formatting --- .../ValueTrackedTreeItemViewModel.cs | 1 - .../ValueTrackingCommandHandler.cs | 24 +++++++++---------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs index a286cc588bfe2..e2e1bdfcd5761 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs @@ -27,7 +27,6 @@ internal class ValueTrackedTreeItemViewModel : ValueTrackingTreeItemViewModel private readonly IValueTrackingService _valueTrackingService; private readonly ValueTrackedItem _trackedItem; - public ValueTrackedTreeItemViewModel( ValueTrackedItem trackedItem, Solution solution, diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs index 434617c890be0..6d1766a43543b 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs @@ -87,19 +87,19 @@ public bool ExecuteCommand(ValueTrackingEditorCommandArgs args, CommandExecution } _threadingContext.JoinableTaskFactory.RunAsync(async () => - { - var selectedSymbol = await GetSelectedSymbolAsync(textSpan, document, cancellationToken).ConfigureAwait(false); - if (selectedSymbol is null) { - // TODO: Show error dialog - return; - } - - var syntaxTree = document.GetRequiredSyntaxTreeSynchronously(cancellationToken); - var location = Location.Create(syntaxTree, textSpan); - - await ShowToolWindowAsync(args.TextView, selectedSymbol, location, document.Project.Solution, cancellationToken).ConfigureAwait(false); - }); + var selectedSymbol = await GetSelectedSymbolAsync(textSpan, document, cancellationToken).ConfigureAwait(false); + if (selectedSymbol is null) + { + // TODO: Show error dialog + return; + } + + var syntaxTree = document.GetRequiredSyntaxTreeSynchronously(cancellationToken); + var location = Location.Create(syntaxTree, textSpan); + + await ShowToolWindowAsync(args.TextView, selectedSymbol, location, document.Project.Solution, cancellationToken).ConfigureAwait(false); + }); return true; } From adb3873c95c8a43bc7ebc719b9c21141f5ce99ed Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Sat, 20 Mar 2021 13:03:12 -0700 Subject: [PATCH 020/127] Update interface to take in textspan --- .../ValueTracking/IValueTrackingService.cs | 9 ++-- .../ValueTracking/ValueTrackingService.cs | 33 +++++++++---- .../AbstractBaseValueTrackingTests.cs | 19 ++------ .../ValueTracking/CSharpValueTrackingTests.cs | 47 +++++++++++++++++++ 4 files changed, 81 insertions(+), 27 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs index 0097f80a96e5a..5e7d0e8c62e6f 100644 --- a/src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs @@ -6,15 +6,16 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.ValueTracking { internal interface IValueTrackingService : IWorkspaceService { - Task> TrackValueSourceAsync(Solution solution, ISymbol symbol, CancellationToken cancellationToken); - Task TrackValueSourceAsync(Solution solution, ISymbol symbol, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken); + Task> TrackValueSourceAsync(TextSpan selection, Document document, CancellationToken cancellationToken); + Task TrackValueSourceAsync(TextSpan selection, Document document, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken); - Task> TrackValueSourceAsync(Solution solution, ValueTrackedItem previousTrackedItem, CancellationToken cancellationToken); - Task TrackValueSourceAsync(Solution solution, ValueTrackedItem previousTrackedItem, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken); + Task> TrackValueSourceAsync(ValueTrackedItem previousTrackedItem, CancellationToken cancellationToken); + Task TrackValueSourceAsync(ValueTrackedItem previousTrackedItem, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken); } } diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 9effa93548c32..8cf3c1355d894 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -9,7 +9,9 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.ValueTracking { @@ -23,27 +25,31 @@ public ValueTrackingService() } public async Task> TrackValueSourceAsync( - Solution solution, - ISymbol symbol, + TextSpan selection, + Document document, CancellationToken cancellationToken) { var progressTracker = new ValueTrackingProgressCollector(); - await TrackValueSourceAsync(solution, symbol, progressTracker, cancellationToken).ConfigureAwait(false); + await TrackValueSourceAsync(selection, document, progressTracker, cancellationToken).ConfigureAwait(false); return progressTracker.GetItems(); } public async Task TrackValueSourceAsync( - Solution solution, - ISymbol symbol, + TextSpan selection, + Document document, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) { + var symbol = await GetSelectedSymbolAsync(selection, document, cancellationToken).ConfigureAwait(false); + if (symbol is IPropertySymbol or IFieldSymbol or ILocalSymbol or IParameterSymbol) { + var solution = document.Project.Solution; + // Add all initializations of the symbol. Those are not caught in // the reference finder but should still show up in the tree foreach (var syntaxRef in symbol.DeclaringSyntaxReferences) @@ -64,17 +70,15 @@ await SymbolFinder.FindReferencesAsync( } public async Task> TrackValueSourceAsync( - Solution solution, ValueTrackedItem previousTrackedItem, CancellationToken cancellationToken) { var progressTracker = new ValueTrackingProgressCollector(); - await TrackValueSourceAsync(solution, previousTrackedItem, progressTracker, cancellationToken).ConfigureAwait(false); + await TrackValueSourceAsync(previousTrackedItem, progressTracker, cancellationToken).ConfigureAwait(false); return progressTracker.GetItems(); } public Task TrackValueSourceAsync( - Solution solution, ValueTrackedItem previousTrackedItem, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) @@ -82,6 +86,19 @@ public Task TrackValueSourceAsync( return Task.CompletedTask; } + private static async Task GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) + { + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var selectedNode = root.FindNode(textSpan); + + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var selectedSymbol = + semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol + ?? semanticModel.GetDeclaredSymbol(selectedNode, cancellationToken); + + return selectedSymbol; + } + private class FindReferencesProgress : IStreamingFindReferencesProgress, IStreamingProgressTracker { private readonly ValueTrackingProgressCollector _valueTrackingProgressCollector; diff --git a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs index 7e0719f1c0e3b..c3407e2d72250 100644 --- a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs @@ -20,26 +20,15 @@ internal static async Task> GetTrackedItemsAsyn var cursorDocument = testWorkspace.DocumentWithCursor; var document = testWorkspace.CurrentSolution.GetRequiredDocument(cursorDocument.Id); var textSpan = new TextSpan(cursorDocument.CursorPosition!.Value, 0); - var symbol = await GetSelectedSymbolAsync(textSpan, document, cancellationToken); var service = testWorkspace.Services.GetRequiredService(); - return await service.TrackValueSourceAsync(testWorkspace.CurrentSolution, symbol, cancellationToken); + return await service.TrackValueSourceAsync(textSpan, document, cancellationToken); } - internal static async Task GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) + internal static async Task> GetTrackedItemsAsync(TestWorkspace testWorkspace, ValueTrackedItem item, CancellationToken cancellationToken = default) { - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var selectedNode = root.FindNode(textSpan); - - Assert.NotNull(selectedNode); - - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var selectedSymbol = - semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol - ?? semanticModel.GetDeclaredSymbol(selectedNode, cancellationToken); - - Assert.NotNull(selectedSymbol); - return selectedSymbol!; + var service = testWorkspace.Services.GetRequiredService(); + return await service.TrackValueSourceAsync(item, cancellationToken); } internal static void ValidateItem(ValueTrackedItem item, int line) diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs index d55feb0426cea..b5cd19366f16b 100644 --- a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -180,5 +180,52 @@ public int Add(int x, int y) var initialItems = await GetTrackedItemsAsync(workspace); Assert.Empty(initialItems); } + + [Fact] + public async Task PropertyAssignment1() + { + var code = +@" +class C +{ + public string S { get; set; } = """"""; + + public void SetS(string s) + { + S$$ = s; + } + + public string GetS() => S; +} + +class Other +{ + public void CallS(C c, string s) + { + c.SetS(s); + } + + public void CallS(C c) + { + CallS(c, ""defaultstring""); + } +} +"; + using var workspace = TestWorkspace.CreateCSharp(code); + var initialItems = await GetTrackedItemsAsync(workspace); + + // + // S = s; [Code.cs:7] + // |> S = [|s|] [Code.cs:7] + // |> public void [|SetS|](string s) [Code.cs:5] + // |> c.SetS(s); [Code.cs:17] + // |> S = ""; [Code.cs:4] + Assert.Equal(2, initialItems.Length); + ValidateItem(initialItems[0], 7); + ValidateItem(initialItems[1], 3); + + //var items = await GetTrackedItemsAsync(workspace, initialItems[0]); + //Assert.Equal(1, items.Length); + } } } From 4d93147010e97e283c3ca8882fa256472c53525f Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Wed, 24 Mar 2021 16:15:48 -0700 Subject: [PATCH 021/127] More tests, now covering across method calls --- .../Core/ValueTracking/ValueTrackedItem.cs | 6 + .../ValueTrackingProgressCollector.cs | 16 ++ ...eTrackingService.FindReferencesProgress.cs | 170 ++++++++++++ .../ValueTracking/ValueTrackingService.cs | 253 +++++++++++++++--- .../AbstractBaseValueTrackingTests.cs | 50 +++- .../ValueTracking/CSharpValueTrackingTests.cs | 214 ++++++++++++++- .../ValueTrackedTreeItemViewModel.cs | 1 - .../ValueTrackingCommandHandler.cs | 2 +- 8 files changed, 657 insertions(+), 55 deletions(-) create mode 100644 src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs index 3fff8bac5048f..901170e2d65ed 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs @@ -43,6 +43,12 @@ private ValueTrackedItem( Document = document; } + public override string ToString() + { + var subText = SourceText.GetSubText(Span); + return subText.ToString(); + } + public static async Task TryCreateAsync(Solution solution, Location location, ISymbol symbol, ValueTrackedItem? parent = null, CancellationToken cancellationToken = default) { Contract.ThrowIfNull(location.SourceTree); diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingProgressCollector.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingProgressCollector.cs index b4061f4ba1fe7..5794dba89218f 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingProgressCollector.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingProgressCollector.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; namespace Microsoft.CodeAnalysis.ValueTracking { @@ -15,6 +17,8 @@ internal class ValueTrackingProgressCollector : IProgress public event EventHandler? OnNewItem; + internal ValueTrackedItem? Parent { get; set; } + public void Report(ValueTrackedItem item) { lock (_lock) @@ -32,5 +36,17 @@ public ImmutableArray GetItems() return _items.ToImmutableArray(); } } + + internal async Task TryReportAsync(Solution solution, Location location, ISymbol symbol, CancellationToken cancellationToken = default) + { + var item = await ValueTrackedItem.TryCreateAsync(solution, location, symbol, Parent, cancellationToken).ConfigureAwait(false); + if (item is not null) + { + Report(item); + return true; + } + + return false; + } } } diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs new file mode 100644 index 0000000000000..278c83574a1f3 --- /dev/null +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs @@ -0,0 +1,170 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Reflection.Metadata; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.LanguageServices; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.ValueTracking +{ + internal partial class ValueTrackingService + { + private class FindReferencesProgress : IStreamingFindReferencesProgress, IStreamingProgressTracker + { + private readonly CancellationToken _cancellationToken; + private readonly ValueTrackingProgressCollector _valueTrackingProgressCollector; + public FindReferencesProgress(ValueTrackingProgressCollector valueTrackingProgressCollector, CancellationToken cancellationToken = default) + { + _valueTrackingProgressCollector = valueTrackingProgressCollector; + _cancellationToken = cancellationToken; + } + + public IStreamingProgressTracker ProgressTracker => this; + + public ValueTask AddItemsAsync(int count) => new(); + + public ValueTask ItemCompletedAsync() => new(); + + public ValueTask OnCompletedAsync() => new(); + + public ValueTask OnDefinitionFoundAsync(ISymbol symbol) => new(); + + public ValueTask OnFindInDocumentCompletedAsync(Document document) => new(); + + public ValueTask OnFindInDocumentStartedAsync(Document document) => new(); + + public async ValueTask OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation location) + { + if (!location.Location.IsInSource) + { + return; + } + + var solution = location.Document.Project.Solution; + + if (symbol is IMethodSymbol methodSymbol) + { + if (methodSymbol.IsConstructor()) + { + await TrackConstructorAsync(location).ConfigureAwait(false); + } + else + { + // If we're searching for references to a method, we don't want to store the symbol as that method again. Instead + // we want to track the invocations and how to follow their source + await TrackMethodInvocationArgumentsAsync(location).ConfigureAwait(false); + } + } + else if (location.IsWrittenTo) + { + var syntaxFacts = location.Document.GetRequiredLanguageService(); + var node = location.Location.FindNode(CancellationToken.None); + + if (syntaxFacts.IsLeftSideOfAnyAssignment(node)) + { + await AddItemsFromAssignmentAsync(location.Document, node, _valueTrackingProgressCollector, _cancellationToken).ConfigureAwait(false); + } + else + { + await _valueTrackingProgressCollector.TryReportAsync(solution, location.Location, symbol, _cancellationToken).ConfigureAwait(false); + } + } + } + + public ValueTask OnStartedAsync() => new(); + + private async Task TrackConstructorAsync(ReferenceLocation referenceLocation) + { + var document = referenceLocation.Document; + var span = referenceLocation.Location.SourceSpan; + + var syntaxRoot = await document.GetRequiredSyntaxRootAsync(_cancellationToken).ConfigureAwait(false); + var originalNode = syntaxRoot.FindNode(span); + + if (originalNode is null) + { + return; + } + + var semanticModel = await document.GetRequiredSemanticModelAsync(_cancellationToken).ConfigureAwait(false); + var operation = semanticModel.GetOperation(originalNode.Parent, _cancellationToken); + if (operation is not IObjectCreationOperation objectCreationOperation) + { + return; + } + + await TrackArgumentsAsync(objectCreationOperation.Arguments, document).ConfigureAwait(false); + } + + private async Task TrackMethodInvocationArgumentsAsync(ReferenceLocation referenceLocation) + { + var document = referenceLocation.Document; + var span = referenceLocation.Location.SourceSpan; + + var syntaxRoot = await document.GetRequiredSyntaxRootAsync(_cancellationToken).ConfigureAwait(false); + var originalNode = syntaxRoot.FindNode(span); + + if (originalNode is null) + { + return; + } + + var syntaxFacts = document.GetRequiredLanguageService(); + var invocationSyntax = originalNode.FirstAncestorOrSelf(syntaxFacts.IsInvocationExpression); + if (invocationSyntax is null) + { + return; + } + + var semanticModel = await document.GetRequiredSemanticModelAsync(_cancellationToken).ConfigureAwait(false); + var operation = semanticModel.GetOperation(invocationSyntax, _cancellationToken); + if (operation is not IInvocationOperation invocationOperation) + { + return; + } + + await TrackArgumentsAsync(invocationOperation.Arguments, document).ConfigureAwait(false); + } + + private async Task TrackArgumentsAsync(ImmutableArray argumentOperations, Document document) + { + var collectorsAndArgumentMap = argumentOperations + .Select(argument => (collector: CreateCollector(), argument)) + .ToImmutableArray(); + + var tasks = collectorsAndArgumentMap + .Select(pair => TrackExpressionAsync(pair.argument.Value, document, pair.collector, _cancellationToken)); + + await Task.WhenAll(tasks).ConfigureAwait(false); + + var items = collectorsAndArgumentMap + .Select(pair => pair.collector) + .SelectMany(collector => collector.GetItems()) + .Reverse(); // ProgressCollector uses a Stack, and we want to maintain the order by arguments, so reverse + + foreach (var item in items) + { + _valueTrackingProgressCollector.Report(item); + } + + ValueTrackingProgressCollector CreateCollector() + { + var collector = new ValueTrackingProgressCollector(); + collector.Parent = _valueTrackingProgressCollector.Parent; + return collector; + } + } + } + } +} diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 8cf3c1355d894..4d0ad2eb45e4d 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -5,18 +5,27 @@ using System; using System.Collections.Immutable; using System.Composition; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Reflection.Metadata; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Elfie.Model; using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.FlowAnalysis; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServices; +using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ValueTracking { [ExportWorkspaceService(typeof(IValueTrackingService)), Shared] - internal class ValueTrackingService : IValueTrackingService + internal partial class ValueTrackingService : IValueTrackingService { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -40,7 +49,7 @@ public async Task TrackValueSourceAsync( ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) { - var symbol = await GetSelectedSymbolAsync(selection, document, cancellationToken).ConfigureAwait(false); + var (symbol, node) = await GetSelectedSymbolAsync(selection, document, cancellationToken).ConfigureAwait(false); if (symbol is IPropertySymbol @@ -48,24 +57,33 @@ or IFieldSymbol or ILocalSymbol or IParameterSymbol) { + RoslynDebug.AssertNotNull(node); + var solution = document.Project.Solution; + var declaringSyntaxReferences = symbol.DeclaringSyntaxReferences; + var syntaxFacts = document.GetRequiredLanguageService(); - // Add all initializations of the symbol. Those are not caught in - // the reference finder but should still show up in the tree - foreach (var syntaxRef in symbol.DeclaringSyntaxReferences) + // If the selection is within a declaration of the symbol, we want to include + // all declarations and assignments of the symbol + if (declaringSyntaxReferences.Any(r => r.Span.IntersectsWith(selection))) { - var location = Location.Create(syntaxRef.SyntaxTree, syntaxRef.Span); - var item = await ValueTrackedItem.TryCreateAsync(solution, location, symbol, parent: null, cancellationToken).ConfigureAwait(false); - if (item is not null) + // Add all initializations of the symbol. Those are not caught in + // the reference finder but should still show up in the tree + foreach (var syntaxRef in declaringSyntaxReferences) { - progressCollector.Report(item); + var location = Location.Create(syntaxRef.SyntaxTree, syntaxRef.Span); + await progressCollector.TryReportAsync(solution, location, symbol, cancellationToken).ConfigureAwait(false); } - } - var findReferenceProgressCollector = new FindReferencesProgress(progressCollector); - await SymbolFinder.FindReferencesAsync( - symbol, solution, findReferenceProgressCollector, - documents: null, FindReferencesSearchOptions.Default, cancellationToken).ConfigureAwait(false); + await TrackVariableSymbolAsync(symbol, document, progressCollector, cancellationToken).ConfigureAwait(false); + } + // The selection is not on a declaration, check that the node + // is on the left side of an assignment. If so, populate so we can + // track the RHS values that contribute to this value + else if (syntaxFacts.IsLeftSideOfAnyAssignment(node)) + { + await AddItemsFromAssignmentAsync(document, node, progressCollector, cancellationToken).ConfigureAwait(false); + } } } @@ -78,63 +96,212 @@ public async Task> TrackValueSourceAsync( return progressTracker.GetItems(); } - public Task TrackValueSourceAsync( + public async Task TrackValueSourceAsync( ValueTrackedItem previousTrackedItem, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) { - return Task.CompletedTask; + progressCollector.Parent = previousTrackedItem; + + switch (previousTrackedItem.Symbol) + { + case ILocalSymbol: + case IPropertySymbol: + case IFieldSymbol: + { + // The "output" is a variable assignment, track places where it gets assigned + await TrackVariableSymbolAsync(previousTrackedItem.Symbol, previousTrackedItem.Document, progressCollector, cancellationToken).ConfigureAwait(false); + } + break; + + case IParameterSymbol parameterSymbol: + { + // The "output" is method calls, so track where this method is invoked for the parameter + await TrackParameterSymbolAsync(parameterSymbol, previousTrackedItem.Document, progressCollector, cancellationToken).ConfigureAwait(false); + } + break; + + case IMethodSymbol methodSymbol: + { + // The "output" is from a method, meaning it has a return our out param that is used. Track those + await TrackMethodSymbolAsync(methodSymbol, previousTrackedItem.Document, progressCollector, cancellationToken).ConfigureAwait(false); + } + break; + } } - private static async Task GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) + private static async Task TrackVariableSymbolAsync(ISymbol symbol, Document document, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) { - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var selectedNode = root.FindNode(textSpan); - - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var selectedSymbol = - semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol - ?? semanticModel.GetDeclaredSymbol(selectedNode, cancellationToken); + var findReferenceProgressCollector = new FindReferencesProgress(progressCollector); + await SymbolFinder.FindReferencesAsync( + symbol, + document.Project.Solution, + findReferenceProgressCollector, + documents: null, FindReferencesSearchOptions.Default, cancellationToken).ConfigureAwait(false); + } - return selectedSymbol; + private static async Task TrackParameterSymbolAsync( + IParameterSymbol parameterSymbol, + Document document, + ValueTrackingProgressCollector progressCollector, + CancellationToken cancellationToken) + { + var containingMethod = (IMethodSymbol)parameterSymbol.ContainingSymbol; + var findReferenceProgressCollector = new FindReferencesProgress(progressCollector); + await SymbolFinder.FindReferencesAsync( + containingMethod, + document.Project.Solution, + findReferenceProgressCollector, + documents: null, FindReferencesSearchOptions.Default, cancellationToken).ConfigureAwait(false); } - private class FindReferencesProgress : IStreamingFindReferencesProgress, IStreamingProgressTracker + private static async Task TrackMethodSymbolAsync(IMethodSymbol methodSymbol, Document document, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) { - private readonly ValueTrackingProgressCollector _valueTrackingProgressCollector; - public FindReferencesProgress(ValueTrackingProgressCollector valueTrackingProgressCollector) + var hasAnyOutData = HasAValueReturn(methodSymbol) || HasAnOutOrRefParam(methodSymbol); + if (!hasAnyOutData) { - _valueTrackingProgressCollector = valueTrackingProgressCollector; + // With no out data, there's nothing to do here + return; } - public IStreamingProgressTracker ProgressTracker => this; + // TODO: Use DFA to find meaningful returns? https://github.com/dotnet/roslyn-analyzers/blob/9e5f533cbafcc5579e4d758bc9bde27b7611ca54/docs/Writing%20dataflow%20analysis%20based%20analyzers.md + if (HasAValueReturn(methodSymbol)) + { + foreach (var location in methodSymbol.GetDefinitionLocationsToShow()) + { + if (location.SourceTree is null) + { + continue; + } + + var node = location.FindNode(cancellationToken); + var sourceDoc = document.Project.Solution.GetRequiredDocument(location.SourceTree); + var syntaxFacts = sourceDoc.GetRequiredLanguageService(); + var returnStatements = node.DescendantNodesAndSelf().Where(n => syntaxFacts.IsReturnStatement(n)); + var semanticModel = await sourceDoc.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + foreach (var returnStatement in returnStatements) + { + var expression = syntaxFacts.GetExpressionOfReturnStatement(returnStatement); + if (expression is null) + { + continue; + } + + var operation = semanticModel.GetOperation(expression, cancellationToken); + if (operation is null) + { + continue; + } - public ValueTask AddItemsAsync(int count) => new(); + await TrackExpressionAsync(operation, sourceDoc, progressCollector, cancellationToken).ConfigureAwait(false); + } + } + } - public ValueTask ItemCompletedAsync() => new(); + if (HasAnOutOrRefParam(methodSymbol)) + { + foreach (var outOrRefParam in methodSymbol.Parameters.Where(p => p.IsRefOrOut())) + { + if (!outOrRefParam.IsFromSource()) + { + continue; + } - public ValueTask OnCompletedAsync() => new(); + await TrackVariableSymbolAsync(outOrRefParam, document, progressCollector, cancellationToken).ConfigureAwait(false); + } + } - public ValueTask OnDefinitionFoundAsync(ISymbol symbol) => new(); + // TODO check for Task + static bool HasAValueReturn(IMethodSymbol methodSymbol) + => methodSymbol.ReturnType.SpecialType != SpecialType.System_Void; - public ValueTask OnFindInDocumentCompletedAsync(Document document) => new(); + static bool HasAnOutOrRefParam(IMethodSymbol methodSymbol) + => methodSymbol.Parameters.Any(p => p.IsRefOrOut()); + } - public ValueTask OnFindInDocumentStartedAsync(Document document) => new(); + private static async Task TrackExpressionAsync(IOperation expressionOperation, Document document, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) + { + if (expressionOperation.Children.Any()) + { + foreach (var childOperation in expressionOperation.Children) + { + await AddOperationAsync(childOperation).ConfigureAwait(false); + } + } + else + { + await AddOperationAsync(expressionOperation).ConfigureAwait(false); + } - public async ValueTask OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation location) + async Task AddOperationAsync(IOperation operation) { - if (location.IsWrittenTo) + var semanticModel = operation.SemanticModel; + if (semanticModel is null) { - var solution = location.Document.Project.Solution; - var item = await ValueTrackedItem.TryCreateAsync(solution, location.Location, symbol, parent: null, CancellationToken.None).ConfigureAwait(false); - if (item is not null) + return; + } + + var symbolInfo = semanticModel.GetSymbolInfo(operation.Syntax, cancellationToken); + if (symbolInfo.Symbol is null) + { + if (operation is ILiteralOperation literalOperation) { - _valueTrackingProgressCollector.Report(item); + await progressCollector.TryReportAsync(document.Project.Solution, + operation.Syntax.GetLocation(), + literalOperation.Type, + cancellationToken: cancellationToken).ConfigureAwait(false); } + + return; } + + await progressCollector.TryReportAsync(document.Project.Solution, + operation.Syntax.GetLocation(), + symbolInfo.Symbol, + cancellationToken: cancellationToken).ConfigureAwait(false); + } + } + + private static async Task<(ISymbol?, SyntaxNode?)> GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) + { + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var selectedNode = root.FindNode(textSpan); + + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var selectedSymbol = + semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol + ?? semanticModel.GetDeclaredSymbol(selectedNode, cancellationToken); + + return (selectedSymbol, selectedNode); + } + + + private static async Task AddItemsFromAssignmentAsync(Document document, SyntaxNode lhsNode, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) + { + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var operation = semanticModel.GetOperation(lhsNode); + if (operation is null) + { + return; + } + + IAssignmentOperation? assignmentOperation = null; + + while (assignmentOperation is null + && operation is not null) + { + assignmentOperation = operation as IAssignmentOperation; + operation = operation.Parent; + } + + if (assignmentOperation is null) + { + return; } - public ValueTask OnStartedAsync() => new(); + var rhsOperation = assignmentOperation.Value; + await TrackExpressionAsync(rhsOperation, document, progressCollector, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs index c3407e2d72250..379ed13a696d5 100644 --- a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs @@ -10,6 +10,7 @@ using System.Threading; using Microsoft.CodeAnalysis.Text; using Xunit; +using System.Collections.Generic; namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking { @@ -31,9 +32,56 @@ internal static async Task> GetTrackedItemsAsyn return await service.TrackValueSourceAsync(item, cancellationToken); } - internal static void ValidateItem(ValueTrackedItem item, int line) + internal static async Task> ValidateChildrenAsync(TestWorkspace testWorkspace, ValueTrackedItem item, int[] lines, CancellationToken cancellationToken = default) + { + var children = await GetTrackedItemsAsync(testWorkspace, item, cancellationToken); + + Assert.Equal(lines.Length, children.Length); + + for (var i = 0; i < lines.Length; i++) + { + ValidateItem(children[i], lines[i]); + } + + return children; + } + + internal static async Task> ValidateChildrenAsync(TestWorkspace testWorkspace, ValueTrackedItem item, (int line, string text)[] childInfo, CancellationToken cancellationToken = default) + { + var children = await GetTrackedItemsAsync(testWorkspace, item, cancellationToken); + + Assert.Equal(childInfo.Length, children.Length); + + for (var i = 0; i < childInfo.Length; i++) + { + ValidateItem(children[i], childInfo[i].line, childInfo[i].text); + } + + return children; + } + + internal static async Task ValidateChildrenEmptyAsync(TestWorkspace testWorkspace, ValueTrackedItem item, CancellationToken cancellationToken = default) + { + var children = await GetTrackedItemsAsync(testWorkspace, item, cancellationToken); + Assert.Empty(children); + } + + internal static async Task ValidateChildrenEmptyAsync(TestWorkspace testWorkspace, IEnumerable items, CancellationToken cancellationToken = default) + { + foreach (var item in items) + { + await ValidateChildrenEmptyAsync(testWorkspace, item, cancellationToken); + } + } + + internal static void ValidateItem(ValueTrackedItem item, int line, string? text = null) { Assert.Equal(line, item.LineSpan.Start); + + if (text is not null) + { + Assert.Equal(text, item.ToString()); + } } } } diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs index b5cd19366f16b..ce68e06a7f977 100644 --- a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -6,6 +6,7 @@ using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Xunit; using Microsoft.CodeAnalysis.Test.Utilities; +using System.Linq; namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking { @@ -182,7 +183,7 @@ public int Add(int x, int y) } [Fact] - public async Task PropertyAssignment1() + public async Task MethodTracking1() { var code = @" @@ -207,7 +208,22 @@ public void CallS(C c, string s) public void CallS(C c) { - CallS(c, ""defaultstring""); + CallS(c, CalculateDefault(c)); + } + + private string CalculateDefault(C c) + { + if (c is null) + { + return ""null""; + } + + if (string.IsNullOrEmpty(c.S)) + { + return ""defaultstring""; + } + + return """"; } } "; @@ -217,15 +233,195 @@ public void CallS(C c) // // S = s; [Code.cs:7] // |> S = [|s|] [Code.cs:7] - // |> public void [|SetS|](string s) [Code.cs:5] - // |> c.SetS(s); [Code.cs:17] - // |> S = ""; [Code.cs:4] - Assert.Equal(2, initialItems.Length); + // |> c.SetS(s); [Code.cs:17] + // |> CallS([|c|], CalculateDefault(c)) [Code.cs:22] + // |> CallS(c, [|CalculateDefault(c)|]) [Code.cs:22] + // |> return "" [Code.cs:37] + // |> return "defaultstring" [Code.cs:34] + // |> return "null" [Code.cs:29] + // + Assert.Equal(1, initialItems.Length); ValidateItem(initialItems[0], 7); - ValidateItem(initialItems[1], 3); - //var items = await GetTrackedItemsAsync(workspace, initialItems[0]); - //Assert.Equal(1, items.Length); + var items = await ValidateChildrenAsync( + workspace, + initialItems.Single(), + lines: new[] + { + 17 // |> c.SetS(s); [Code.cs:17] + }); + + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (22, "c" ), // |> CallS([|c|], CalculateDefault(c)) [Code.cs:22] + (22, "CalculateDefault" ) // |> CallS(c, [|CalculateDefault(c)|]) [Code.cs:22] + }); + + var referenceC = items[0]; + await ValidateChildrenEmptyAsync(workspace, referenceC); + + var calculateDefaultCall = items[1]; + var children = await ValidateChildrenAsync( + workspace, + calculateDefaultCall, + childInfo: new[] + { + (37, "\"\""), // |> return "" [Code.cs:37] + (34, "\"defaultstring\""), // |> return "defaultstring" [Code.cs:34] + (29, "\"null\""), // |> return "null" [Code.cs:29] + }); + + foreach (var child in children) + { + await ValidateChildrenEmptyAsync(workspace, child); + } + } + + [Fact] + public async Task MethodTracking2() + { + var code = +@" +class C +{ + public string S { get; set; } = """"""; + + public void SetS(string s) + { + S$$ = s; + } + + public string GetS() => S; +} + +class Other +{ + private readonly string _adornment; + public Other(string adornment) + { + _adornment = adornment; + } + + public void CallS(C c, string s) + { + c.SetS(s); + } + + public void CallS(C c) + { + CallS(c, CalculateDefault(c) + _adornment); + } + + private string CalculateDefault(C c) + { + if (c is null) + { + return ""null""; + } + + if (string.IsNullOrEmpty(c.S)) + { + return ""defaultstring""; + } + + return """"; + } +} + +class Program +{ + public static void Main(string[] args) + { + var other = new Other(""some value""); + var c = new C(); + other.CallS(c); + } +} +"; + using var workspace = TestWorkspace.CreateCSharp(code); + var initialItems = await GetTrackedItemsAsync(workspace); + + // + // S = s; [Code.cs:7] + // |> S = [|s|] [Code.cs:7] + // |> c.SetS(s); [Code.cs:23] + // |> CallS([|c|], CalculateDefault(c) + _adornment) [Code.cs:28] + // |> other.CallS([|c|]); [Code.cs:53] + // |> CallS(c, CalculateDefault(c) + [|_adornment|]) [Code.cs:28] + // |> _adornment = [|adornment|]; [Code.cs:18] + // |> var other = new Other([|"some value"|]); [Code.cs:51] + // |> CallS(c, [|CalculateDefault(c)|] + _adornment) [Code.cs:28] + // |> return "" [Code.cs:37] + // |> return "defaultstring" [Code.cs:34] + // |> return "null" [Code.cs:29] + // + Assert.Equal(1, initialItems.Length); + ValidateItem(initialItems[0], 7); + + var items = await ValidateChildrenAsync( + workspace, + initialItems.Single(), + lines: new[] + { + 23 // |> c.SetS(s); [Code.cs:23] + }); + + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (28, "c" ), // |> CallS([|c|], CalculateDefault(c) + _adornment) [Code.cs:28] + (28, "_adornment" ), // |> CallS(c, CalculateDefault(c) + [|_adornment|]) [Code.cs:28] + (28, "CalculateDefault(c)" ), // |> CallS(c, [|CalculateDefault|](c) + _adornment) [Code.cs:28] + }); + + var referenceC = items[0]; + var children = await ValidateChildrenAsync( + workspace, + referenceC, + childInfo: new[] + { + (53, "c"), // |> other.CallS([|c|]); [Code.cs:53] + }); + + await ValidateChildrenEmptyAsync(workspace, children); + + var adornmentAssignment = items[1]; + children = await ValidateChildrenAsync( + workspace, + adornmentAssignment, + childInfo: new[] + { + (18, "adornment"), // |> _adornment = [|adornment|] [Code.cs:18] + }); + + children = await ValidateChildrenAsync( + workspace, + children.Single(), + childInfo: new[] + { + (51, "\"some value\"") // |> var other = new Other([|"some value"|]); [Code.cs:51] + }); + await ValidateChildrenEmptyAsync(workspace, children); + + var calculateDefaultCall = items[2]; + children = await ValidateChildrenAsync( + workspace, + calculateDefaultCall, + childInfo: new[] + { + (43, "\"\""), // |> return "" [Code.cs:37] + (40, "\"defaultstring\""), // |> return "defaultstring" [Code.cs:34] + (35, "\"null\""), // |> return "null" [Code.cs:29] + }); + + await ValidateChildrenEmptyAsync(workspace, children); } } } diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs index e2e1bdfcd5761..1ad5ea162fc39 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs @@ -110,7 +110,6 @@ private void CalculateChildren() private async Task> CalculateChildrenAsync(CancellationToken cancellationToken) { var valueTrackedItems = await _valueTrackingService.TrackValueSourceAsync( - _solution, _trackedItem, cancellationToken).ConfigureAwait(false); diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs index 6d1766a43543b..1312e3e2095d5 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs @@ -147,7 +147,7 @@ private async Task ShowToolWindowAsync(ITextView textView, ISymbol selectedSymbo var valueTrackingService = solution.Workspace.Services.GetRequiredService(); var classificationFormatMap = _classificationFormatMapService.GetClassificationFormatMap(textView); - var childItems = await valueTrackingService.TrackValueSourceAsync(solution, item, cancellationToken).ConfigureAwait(false); + var childItems = await valueTrackingService.TrackValueSourceAsync(item, cancellationToken).ConfigureAwait(false); var childViewModels = childItems.SelectAsArray(child => CreateViewModel(child)); RoslynDebug.AssertNotNull(location.SourceTree); From dfda2ef2d2ac34012e0876c6dff6b9527c5dd432 Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Thu, 25 Mar 2021 12:02:30 -0700 Subject: [PATCH 022/127] More methods handled correctly --- .../ValueTracking/ValueTrackingService.cs | 42 +++++++++++-- .../ValueTracking/CSharpValueTrackingTests.cs | 62 +++++++++++++++++++ 2 files changed, 99 insertions(+), 5 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 4d0ad2eb45e4d..6d51585116572 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -84,6 +84,15 @@ or ILocalSymbol { await AddItemsFromAssignmentAsync(document, node, progressCollector, cancellationToken).ConfigureAwait(false); } + // Not on the left part of an assignment? Then just add an item with the statement + // and the symbol. It should be the top item, and children will find the sources + // of the value. A good example is a return statement, such as "return $$x", + // where $$ is the cursor position. The top item should have the return statement for + // context, and the remaining items should expand into the assignments of x + else + { + await progressCollector.TryReportAsync(document.Project.Solution, node.GetLocation(), symbol, cancellationToken).ConfigureAwait(false); + } } } @@ -116,7 +125,9 @@ public async Task TrackValueSourceAsync( case IParameterSymbol parameterSymbol: { - // The "output" is method calls, so track where this method is invoked for the parameter + // The "output" is method calls, so track where this method is invoked for the parameter as + // well as assignments inside the method. Both contribute to the final values + await TrackVariableSymbolAsync(previousTrackedItem.Symbol, previousTrackedItem.Document, progressCollector, cancellationToken).ConfigureAwait(false); await TrackParameterSymbolAsync(parameterSymbol, previousTrackedItem.Document, progressCollector, cancellationToken).ConfigureAwait(false); } break; @@ -177,15 +188,17 @@ private static async Task TrackMethodSymbolAsync(IMethodSymbol methodSymbol, Doc var node = location.FindNode(cancellationToken); var sourceDoc = document.Project.Solution.GetRequiredDocument(location.SourceTree); var syntaxFacts = sourceDoc.GetRequiredLanguageService(); - var returnStatements = node.DescendantNodesAndSelf().Where(n => syntaxFacts.IsReturnStatement(n)); + var returnStatements = node.DescendantNodesAndSelf().Where(n => syntaxFacts.IsReturnStatement(n)).ToImmutableArray(); var semanticModel = await sourceDoc.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - foreach (var returnStatement in returnStatements) + if (returnStatements.IsDefaultOrEmpty) { - var expression = syntaxFacts.GetExpressionOfReturnStatement(returnStatement); + // If there are no return statements and the method has a return type, then the method body is an expression + // and we're interested in parsing that expression + var expression = node.DescendantNodesAndSelf().First(syntaxFacts.IsMethodBody); if (expression is null) { - continue; + return; } var operation = semanticModel.GetOperation(expression, cancellationToken); @@ -196,6 +209,25 @@ private static async Task TrackMethodSymbolAsync(IMethodSymbol methodSymbol, Doc await TrackExpressionAsync(operation, sourceDoc, progressCollector, cancellationToken).ConfigureAwait(false); } + else + { + foreach (var returnStatement in returnStatements) + { + var expression = syntaxFacts.GetExpressionOfReturnStatement(returnStatement); + if (expression is null) + { + continue; + } + + var operation = semanticModel.GetOperation(expression, cancellationToken); + if (operation is null) + { + continue; + } + + await TrackExpressionAsync(operation, sourceDoc, progressCollector, cancellationToken).ConfigureAwait(false); + } + } } } diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs index ce68e06a7f977..0b118c6dd152b 100644 --- a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -423,5 +423,67 @@ public static void Main(string[] args) await ValidateChildrenEmptyAsync(workspace, children); } + + [Fact] + public async Task TestMethodTracking3() + { + var code = +@" +using System.Threading.Tasks; + +namespace N +{ + class C + { + public int Add(int x, int y) + { + x += y; + return x; + } + + public Task AddAsync(int x, int y) => Task.FromResult(Add(x,y)); + + public async Task Double(int x) + { + x = await AddAsync(x, x); + return $$x; + } + } +}"; + // + // |> return [|x|] [Code.cs:18] + // |> x = await [|AddAsync(x, x)|] [Code.cs:17] + // |> Task.FromResult(Add(x, y)) [Code.cs:13] + // |> return x [Code.cs:11] + using var workspace = TestWorkspace.CreateCSharp(code); + var initialItems = await GetTrackedItemsAsync(workspace); + Assert.Equal(1, initialItems.Length); + ValidateItem(initialItems.Single(), 18, "x"); // |> return [|x|] [Code.cs:18] + + var children = await ValidateChildrenAsync( + workspace, + initialItems.Single(), + childInfo: new[] + { + (17, "AddAsync(x, x)") // |> x = await [|AddAsync(x, x)|] [Code.cs:17] + }); + + children = await ValidateChildrenAsync( + workspace, + children.Single(), + childInfo: new[] + { + (13, "Task.FromResult(Add(x,y))") // |> Task.FromResult(Add(x, y)) + }); + + // TODO: This doesn't work right now... + //children = await ValidateChildrenAsync( + // workspace, + // children.Single(), + // childInfo: new[] + // { + // (11, "x") // |> return x [Code.cs:11] + // }); + } } } From 326f9506598a418d08d250b67e88d31a4d0e5da8 Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Thu, 25 Mar 2021 14:57:47 -0700 Subject: [PATCH 023/127] Fixed arguments for invocation expressions --- ...eTrackingService.FindReferencesProgress.cs | 35 +----- .../ValueTracking/ValueTrackingService.cs | 110 +++++++++++++----- .../ValueTracking/CSharpValueTrackingTests.cs | 23 ++-- 3 files changed, 96 insertions(+), 72 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs index 278c83574a1f3..60bb3f09c508d 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs @@ -92,7 +92,7 @@ private async Task TrackConstructorAsync(ReferenceLocation referenceLocation) var syntaxRoot = await document.GetRequiredSyntaxRootAsync(_cancellationToken).ConfigureAwait(false); var originalNode = syntaxRoot.FindNode(span); - if (originalNode is null) + if (originalNode is null || originalNode.Parent is null) { return; } @@ -104,7 +104,7 @@ private async Task TrackConstructorAsync(ReferenceLocation referenceLocation) return; } - await TrackArgumentsAsync(objectCreationOperation.Arguments, document).ConfigureAwait(false); + await TrackArgumentsAsync(objectCreationOperation.Arguments, document, _valueTrackingProgressCollector, _cancellationToken).ConfigureAwait(false); } private async Task TrackMethodInvocationArgumentsAsync(ReferenceLocation referenceLocation) @@ -134,36 +134,7 @@ private async Task TrackMethodInvocationArgumentsAsync(ReferenceLocation referen return; } - await TrackArgumentsAsync(invocationOperation.Arguments, document).ConfigureAwait(false); - } - - private async Task TrackArgumentsAsync(ImmutableArray argumentOperations, Document document) - { - var collectorsAndArgumentMap = argumentOperations - .Select(argument => (collector: CreateCollector(), argument)) - .ToImmutableArray(); - - var tasks = collectorsAndArgumentMap - .Select(pair => TrackExpressionAsync(pair.argument.Value, document, pair.collector, _cancellationToken)); - - await Task.WhenAll(tasks).ConfigureAwait(false); - - var items = collectorsAndArgumentMap - .Select(pair => pair.collector) - .SelectMany(collector => collector.GetItems()) - .Reverse(); // ProgressCollector uses a Stack, and we want to maintain the order by arguments, so reverse - - foreach (var item in items) - { - _valueTrackingProgressCollector.Report(item); - } - - ValueTrackingProgressCollector CreateCollector() - { - var collector = new ValueTrackingProgressCollector(); - collector.Parent = _valueTrackingProgressCollector.Parent; - return collector; - } + await TrackArgumentsAsync(invocationOperation.Arguments, document, _valueTrackingProgressCollector, _cancellationToken).ConfigureAwait(false); } } } diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 6d51585116572..c97ef020103c7 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -11,6 +11,7 @@ using System.Reflection.Metadata; using System.Threading; using System.Threading.Tasks; +using System.Xml.Linq; using Microsoft.CodeAnalysis.Elfie.Model; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.FlowAnalysis; @@ -258,40 +259,12 @@ private static async Task TrackExpressionAsync(IOperation expressionOperation, D { foreach (var childOperation in expressionOperation.Children) { - await AddOperationAsync(childOperation).ConfigureAwait(false); + await AddOperationAsync(childOperation, document, progressCollector, cancellationToken).ConfigureAwait(false); } } else { - await AddOperationAsync(expressionOperation).ConfigureAwait(false); - } - - async Task AddOperationAsync(IOperation operation) - { - var semanticModel = operation.SemanticModel; - if (semanticModel is null) - { - return; - } - - var symbolInfo = semanticModel.GetSymbolInfo(operation.Syntax, cancellationToken); - if (symbolInfo.Symbol is null) - { - if (operation is ILiteralOperation literalOperation) - { - await progressCollector.TryReportAsync(document.Project.Solution, - operation.Syntax.GetLocation(), - literalOperation.Type, - cancellationToken: cancellationToken).ConfigureAwait(false); - } - - return; - } - - await progressCollector.TryReportAsync(document.Project.Solution, - operation.Syntax.GetLocation(), - symbolInfo.Symbol, - cancellationToken: cancellationToken).ConfigureAwait(false); + await AddOperationAsync(expressionOperation, document, progressCollector, cancellationToken).ConfigureAwait(false); } } @@ -308,6 +281,54 @@ await progressCollector.TryReportAsync(document.Project.Solution, return (selectedSymbol, selectedNode); } + private static async Task AddOperationAsync(IOperation operation, Document document, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) + { + var semanticModel = operation.SemanticModel; + if (semanticModel is null) + { + return; + } + + var symbolInfo = semanticModel.GetSymbolInfo(operation.Syntax, cancellationToken); + if (symbolInfo.Symbol is null) + { + if (operation is ILiteralOperation { Type: not null } literalOperation) +{ + await progressCollector.TryReportAsync(document.Project.Solution, + operation.Syntax.GetLocation(), + literalOperation.Type!, + cancellationToken: cancellationToken).ConfigureAwait(false); + } + + return; + } + + if (operation is IInvocationOperation invocationOperation) + { + // If the operation is an invocation, we want to find if invocations are part of the arguments as well + // and make sure they are added + foreach (var argument in invocationOperation.Arguments) + { + if (argument.Value is IInvocationOperation argumentInvocationOperation) + { + await AddOperationAsync(argumentInvocationOperation, document, progressCollector, cancellationToken).ConfigureAwait(false); + } + } + } + else if (operation is IReturnOperation returnOperation) + { + // For return operations we want to track + // the value returned in case it has invocations + // or other items that need to be handled special + await AddOperationAsync(returnOperation.ReturnedValue, document, progressCollector, cancellationToken).ConfigureAwait(false); + return; + } + + await progressCollector.TryReportAsync(document.Project.Solution, + operation.Syntax.GetLocation(), + symbolInfo.Symbol, + cancellationToken: cancellationToken).ConfigureAwait(false); + } private static async Task AddItemsFromAssignmentAsync(Document document, SyntaxNode lhsNode, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) { @@ -335,5 +356,34 @@ private static async Task AddItemsFromAssignmentAsync(Document document, SyntaxN var rhsOperation = assignmentOperation.Value; await TrackExpressionAsync(rhsOperation, document, progressCollector, cancellationToken).ConfigureAwait(false); } + + private static async Task TrackArgumentsAsync(ImmutableArray argumentOperations, Document document, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) + { + var collectorsAndArgumentMap = argumentOperations + .Select(argument => (collector: CreateCollector(), argument)) + .ToImmutableArray(); + + var tasks = collectorsAndArgumentMap + .Select(pair => TrackExpressionAsync(pair.argument.Value, document, pair.collector, cancellationToken)); + + await Task.WhenAll(tasks).ConfigureAwait(false); + + var items = collectorsAndArgumentMap + .Select(pair => pair.collector) + .SelectMany(collector => collector.GetItems()) + .Reverse(); // ProgressCollector uses a Stack, and we want to maintain the order by arguments, so reverse + + foreach (var item in items) + { + progressCollector.Report(item); + } + + ValueTrackingProgressCollector CreateCollector() + { + var collector = new ValueTrackingProgressCollector(); + collector.Parent = progressCollector.Parent; + return collector; + } + } } } diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs index 0b118c6dd152b..d3070b317ecde 100644 --- a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -453,7 +453,8 @@ public async Task Double(int x) // // |> return [|x|] [Code.cs:18] // |> x = await [|AddAsync(x, x)|] [Code.cs:17] - // |> Task.FromResult(Add(x, y)) [Code.cs:13] + // |> [|Task.FromResult|](Add(x, y)) [Code.cs:13] + // |> Task.FromResult([|Add(x, y)|]) [Code.cs:13] // |> return x [Code.cs:11] using var workspace = TestWorkspace.CreateCSharp(code); var initialItems = await GetTrackedItemsAsync(workspace); @@ -473,17 +474,19 @@ public async Task Double(int x) children.Single(), childInfo: new[] { - (13, "Task.FromResult(Add(x,y))") // |> Task.FromResult(Add(x, y)) + (13, "Task.FromResult(Add(x,y))"), // |> [|Task.FromResult|](Add(x, y)) [Code.cs:13] + (13, "Add(x,y)") // |> Task.FromResult([|Add(x, y)|]) [Code.cs:13] }); - // TODO: This doesn't work right now... - //children = await ValidateChildrenAsync( - // workspace, - // children.Single(), - // childInfo: new[] - // { - // (11, "x") // |> return x [Code.cs:11] - // }); + await ValidateChildrenEmptyAsync(workspace, children[0]); + + children = await ValidateChildrenAsync( + workspace, + children[1], + childInfo: new[] + { + (10, "x") // |> return x [Code.cs:10] + }); } } } From e28fc847f7565040ce157a91fbffbe21d7a13ebe Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Mon, 29 Mar 2021 12:14:09 -0700 Subject: [PATCH 024/127] Update src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs Co-authored-by: David Wengier --- src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index c97ef020103c7..0dd9495ce2f51 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -135,7 +135,7 @@ public async Task TrackValueSourceAsync( case IMethodSymbol methodSymbol: { - // The "output" is from a method, meaning it has a return our out param that is used. Track those + // The "output" is from a method, meaning it has a return or out param that is used. Track those await TrackMethodSymbolAsync(methodSymbol, previousTrackedItem.Document, progressCollector, cancellationToken).ConfigureAwait(false); } break; From e4ed2b1581d759902837a2128953447555fb84a4 Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Mon, 29 Mar 2021 17:12:49 -0700 Subject: [PATCH 025/127] Correctness fixes --- src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs | 4 ++-- .../Test/ValueTracking/CSharpValueTrackingTests.cs | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index c97ef020103c7..0ce2136cf8453 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -293,7 +293,7 @@ private static async Task AddOperationAsync(IOperation operation, Document docum if (symbolInfo.Symbol is null) { if (operation is ILiteralOperation { Type: not null } literalOperation) -{ + { await progressCollector.TryReportAsync(document.Project.Solution, operation.Syntax.GetLocation(), literalOperation.Type!, @@ -315,7 +315,7 @@ await progressCollector.TryReportAsync(document.Project.Solution, } } } - else if (operation is IReturnOperation returnOperation) + else if (operation is IReturnOperation { ReturnedValue: not null } returnOperation) { // For return operations we want to track // the value returned in case it has invocations diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs index d3070b317ecde..3eaa99f0b9da0 100644 --- a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -251,7 +251,6 @@ private string CalculateDefault(C c) 17 // |> c.SetS(s); [Code.cs:17] }); - items = await ValidateChildrenAsync( workspace, items.Single(), @@ -370,7 +369,6 @@ public static void Main(string[] args) 23 // |> c.SetS(s); [Code.cs:23] }); - items = await ValidateChildrenAsync( workspace, items.Single(), From f66f4c1f60e9401cb2a60fdf97f8c883e3fed5cc Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Tue, 30 Mar 2021 17:25:15 -0700 Subject: [PATCH 026/127] Move to visitor pattern for operations to make it easier to follow --- ...eTrackingService.FindReferencesProgress.cs | 18 +-- .../ValueTrackingService.Visitor.cs | 151 ++++++++++++++++++ .../ValueTracking/ValueTrackingService.cs | 134 +++------------- .../AbstractBaseValueTrackingTests.cs | 18 +-- .../ValueTracking/CSharpValueTrackingTests.cs | 99 +++++++++--- 5 files changed, 256 insertions(+), 164 deletions(-) create mode 100644 src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs index 60bb3f09c508d..0fadea6a379d9 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs @@ -23,10 +23,10 @@ internal partial class ValueTrackingService private class FindReferencesProgress : IStreamingFindReferencesProgress, IStreamingProgressTracker { private readonly CancellationToken _cancellationToken; - private readonly ValueTrackingProgressCollector _valueTrackingProgressCollector; - public FindReferencesProgress(ValueTrackingProgressCollector valueTrackingProgressCollector, CancellationToken cancellationToken = default) + private readonly OperationCollector _operationCollector; + public FindReferencesProgress(OperationCollector valueTrackingProgressCollector, CancellationToken cancellationToken = default) { - _valueTrackingProgressCollector = valueTrackingProgressCollector; + _operationCollector = valueTrackingProgressCollector; _cancellationToken = cancellationToken; } @@ -73,11 +73,11 @@ public async ValueTask OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation l if (syntaxFacts.IsLeftSideOfAnyAssignment(node)) { - await AddItemsFromAssignmentAsync(location.Document, node, _valueTrackingProgressCollector, _cancellationToken).ConfigureAwait(false); + await AddItemsFromAssignmentAsync(location.Document, node, _operationCollector, _cancellationToken).ConfigureAwait(false); } else { - await _valueTrackingProgressCollector.TryReportAsync(solution, location.Location, symbol, _cancellationToken).ConfigureAwait(false); + await _operationCollector.ProgressCollector.TryReportAsync(solution, location.Location, symbol, _cancellationToken).ConfigureAwait(false); } } } @@ -99,12 +99,12 @@ private async Task TrackConstructorAsync(ReferenceLocation referenceLocation) var semanticModel = await document.GetRequiredSemanticModelAsync(_cancellationToken).ConfigureAwait(false); var operation = semanticModel.GetOperation(originalNode.Parent, _cancellationToken); - if (operation is not IObjectCreationOperation objectCreationOperation) + if (operation is not IObjectCreationOperation) { return; } - await TrackArgumentsAsync(objectCreationOperation.Arguments, document, _valueTrackingProgressCollector, _cancellationToken).ConfigureAwait(false); + await _operationCollector.VisitAsync(operation, _cancellationToken).ConfigureAwait(false); } private async Task TrackMethodInvocationArgumentsAsync(ReferenceLocation referenceLocation) @@ -129,12 +129,12 @@ private async Task TrackMethodInvocationArgumentsAsync(ReferenceLocation referen var semanticModel = await document.GetRequiredSemanticModelAsync(_cancellationToken).ConfigureAwait(false); var operation = semanticModel.GetOperation(invocationSyntax, _cancellationToken); - if (operation is not IInvocationOperation invocationOperation) + if (operation is not IInvocationOperation) { return; } - await TrackArgumentsAsync(invocationOperation.Arguments, document, _valueTrackingProgressCollector, _cancellationToken).ConfigureAwait(false); + await _operationCollector.VisitAsync(operation, _cancellationToken).ConfigureAwait(false); } } } diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs new file mode 100644 index 0000000000000..92749788861fa --- /dev/null +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs @@ -0,0 +1,151 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Linq; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Shared.Extensions; +using static Microsoft.CodeAnalysis.EditAndContinue.TraceLog; +using Microsoft.CodeAnalysis.Editor.Implementation.CommentSelection; + +namespace Microsoft.CodeAnalysis.ValueTracking +{ + internal partial class ValueTrackingService + { + private class OperationCollector + { + public ValueTrackingProgressCollector ProgressCollector { get; } + public Solution Solution { get; } + + public OperationCollector(ValueTrackingProgressCollector progressCollector, Solution solution) + { + ProgressCollector = progressCollector; + Solution = solution; + } + + public Task VisitAsync(IOperation operation, CancellationToken cancellationToken) + => operation switch + { + IObjectCreationOperation objectCreationOperation => VisitObjectCreationAsync(objectCreationOperation, cancellationToken), + IInvocationOperation invocationOperation => VisitInvocationAsync(invocationOperation, cancellationToken), + ILiteralOperation literalOperation => VisitLiteralAsync(literalOperation, cancellationToken), + IReturnOperation returnOperation => VisitReturnAsync(returnOperation, cancellationToken), + IFieldReferenceOperation fieldReferenceOperation => VisitFieldReferenceAsync(fieldReferenceOperation, cancellationToken), + _ => VisitDefaultAsync(operation, cancellationToken) + }; + + private async Task VisitDefaultAsync(IOperation operation, CancellationToken cancellationToken) + { + // If the operation has children, always visit the children instead of the root + // operation. They are the interesting bits for ValueTracking + if (operation.Children.Any()) + { + foreach (var childOperation in operation.Children) + { + await VisitAsync(childOperation, cancellationToken).ConfigureAwait(false); + } + + return; + } + + var semanticModel = operation.SemanticModel; + if (semanticModel is null) + { + return; + } + + var symbolInfo = semanticModel.GetSymbolInfo(operation.Syntax, cancellationToken); + if (symbolInfo.Symbol is null) + { + return; + } + + await AddOperationAsync(operation, symbolInfo.Symbol, cancellationToken).ConfigureAwait(false); + } + + private Task VisitFieldReferenceAsync(IFieldReferenceOperation fieldReferenceOperation, CancellationToken cancellationToken) + => AddOperationAsync(fieldReferenceOperation, fieldReferenceOperation.Member, cancellationToken); + + private Task VisitObjectCreationAsync(IObjectCreationOperation objectCreationOperation, CancellationToken cancellationToken) + => TrackArgumentsAsync(objectCreationOperation.Arguments, cancellationToken); + + private async Task VisitInvocationAsync(IInvocationOperation invocationOperation, CancellationToken cancellationToken) + { + await AddOperationAsync(invocationOperation, invocationOperation.TargetMethod, cancellationToken).ConfigureAwait(false); + await TrackArgumentsAsync(invocationOperation.Arguments, cancellationToken).ConfigureAwait(false); + } + + private async Task VisitLiteralAsync(ILiteralOperation literalOperation, CancellationToken cancellationToken) + { + if (literalOperation.Type is null) + { + return; + } + + await AddOperationAsync(literalOperation, literalOperation.Type, cancellationToken).ConfigureAwait(false); + } + + private async Task VisitReturnAsync(IReturnOperation returnOperation, CancellationToken cancellationToken) + { + if (returnOperation.ReturnedValue is null) + { + return; + } + + await VisitAsync(returnOperation.ReturnedValue, cancellationToken).ConfigureAwait(false); + } + + private async Task AddOperationAsync(IOperation operation, ISymbol symbol, CancellationToken cancellationToken) + { + _ = await ProgressCollector.TryReportAsync( + Solution, + operation.Syntax.GetLocation(), + symbol, + cancellationToken: cancellationToken).ConfigureAwait(false); + } + + private async Task TrackArgumentsAsync(ImmutableArray argumentOperations, CancellationToken cancellationToken) + { + var collectorsAndArgumentMap = argumentOperations + .Where(ShouldTrack) + .Select(argument => (collector: Clone(), argument)) + .ToImmutableArray(); + + var tasks = collectorsAndArgumentMap + .Select(pair => pair.collector.VisitAsync(pair.argument.Value, cancellationToken)); + + await Task.WhenAll(tasks).ConfigureAwait(false); + + var items = collectorsAndArgumentMap + .Select(pair => pair.collector.ProgressCollector) + .SelectMany(collector => collector.GetItems()) + .Reverse(); // ProgressCollector uses a Stack, and we want to maintain the order by arguments, so reverse + + foreach (var item in items) + { + ProgressCollector.Report(item); + } + + static bool ShouldTrack(IArgumentOperation argumentOperation) + { + return argumentOperation.Parameter.IsRefOrOut() + || argumentOperation.Value is IExpressionStatementOperation + or IBinaryOperation + or IInvocationOperation + or IParameterReferenceOperation + or ILiteralOperation; + } + } + + private OperationCollector Clone() + { + var collector = new ValueTrackingProgressCollector(); + collector.Parent = ProgressCollector.Parent; + return new OperationCollector(collector, Solution); + } + } + } +} diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 9bb7d5bf6b183..8f436ec9d2d4e 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -59,6 +59,7 @@ or ILocalSymbol or IParameterSymbol) { RoslynDebug.AssertNotNull(node); + var operationCollector = new OperationCollector(progressCollector, document.Project.Solution); var solution = document.Project.Solution; var declaringSyntaxReferences = symbol.DeclaringSyntaxReferences; @@ -76,14 +77,14 @@ or ILocalSymbol await progressCollector.TryReportAsync(solution, location, symbol, cancellationToken).ConfigureAwait(false); } - await TrackVariableSymbolAsync(symbol, document, progressCollector, cancellationToken).ConfigureAwait(false); + await TrackVariableSymbolAsync(symbol, operationCollector, cancellationToken).ConfigureAwait(false); } // The selection is not on a declaration, check that the node // is on the left side of an assignment. If so, populate so we can // track the RHS values that contribute to this value else if (syntaxFacts.IsLeftSideOfAnyAssignment(node)) { - await AddItemsFromAssignmentAsync(document, node, progressCollector, cancellationToken).ConfigureAwait(false); + await AddItemsFromAssignmentAsync(document, node, operationCollector, cancellationToken).ConfigureAwait(false); } // Not on the left part of an assignment? Then just add an item with the statement // and the symbol. It should be the top item, and children will find the sources @@ -112,6 +113,7 @@ public async Task TrackValueSourceAsync( CancellationToken cancellationToken) { progressCollector.Parent = previousTrackedItem; + var operationCollector = new OperationCollector(progressCollector, previousTrackedItem.Document.Project.Solution); switch (previousTrackedItem.Symbol) { @@ -120,7 +122,7 @@ public async Task TrackValueSourceAsync( case IFieldSymbol: { // The "output" is a variable assignment, track places where it gets assigned - await TrackVariableSymbolAsync(previousTrackedItem.Symbol, previousTrackedItem.Document, progressCollector, cancellationToken).ConfigureAwait(false); + await TrackVariableSymbolAsync(previousTrackedItem.Symbol, operationCollector, cancellationToken).ConfigureAwait(false); } break; @@ -128,46 +130,45 @@ public async Task TrackValueSourceAsync( { // The "output" is method calls, so track where this method is invoked for the parameter as // well as assignments inside the method. Both contribute to the final values - await TrackVariableSymbolAsync(previousTrackedItem.Symbol, previousTrackedItem.Document, progressCollector, cancellationToken).ConfigureAwait(false); - await TrackParameterSymbolAsync(parameterSymbol, previousTrackedItem.Document, progressCollector, cancellationToken).ConfigureAwait(false); + await TrackVariableSymbolAsync(previousTrackedItem.Symbol, operationCollector, cancellationToken).ConfigureAwait(false); + await TrackParameterSymbolAsync(parameterSymbol, operationCollector, cancellationToken).ConfigureAwait(false); } break; case IMethodSymbol methodSymbol: { // The "output" is from a method, meaning it has a return or out param that is used. Track those - await TrackMethodSymbolAsync(methodSymbol, previousTrackedItem.Document, progressCollector, cancellationToken).ConfigureAwait(false); + await TrackMethodSymbolAsync(methodSymbol, operationCollector, cancellationToken).ConfigureAwait(false); } break; } } - private static async Task TrackVariableSymbolAsync(ISymbol symbol, Document document, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) + private static async Task TrackVariableSymbolAsync(ISymbol symbol, OperationCollector collector, CancellationToken cancellationToken) { - var findReferenceProgressCollector = new FindReferencesProgress(progressCollector); + var findReferenceProgressCollector = new FindReferencesProgress(collector, cancellationToken: cancellationToken); await SymbolFinder.FindReferencesAsync( symbol, - document.Project.Solution, + collector.Solution, findReferenceProgressCollector, documents: null, FindReferencesSearchOptions.Default, cancellationToken).ConfigureAwait(false); } private static async Task TrackParameterSymbolAsync( IParameterSymbol parameterSymbol, - Document document, - ValueTrackingProgressCollector progressCollector, + OperationCollector collector, CancellationToken cancellationToken) { var containingMethod = (IMethodSymbol)parameterSymbol.ContainingSymbol; - var findReferenceProgressCollector = new FindReferencesProgress(progressCollector); + var findReferenceProgressCollector = new FindReferencesProgress(collector, cancellationToken: cancellationToken); await SymbolFinder.FindReferencesAsync( containingMethod, - document.Project.Solution, + collector.Solution, findReferenceProgressCollector, documents: null, FindReferencesSearchOptions.Default, cancellationToken).ConfigureAwait(false); } - private static async Task TrackMethodSymbolAsync(IMethodSymbol methodSymbol, Document document, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) + private static async Task TrackMethodSymbolAsync(IMethodSymbol methodSymbol, OperationCollector collector, CancellationToken cancellationToken) { var hasAnyOutData = HasAValueReturn(methodSymbol) || HasAnOutOrRefParam(methodSymbol); if (!hasAnyOutData) @@ -187,7 +188,7 @@ private static async Task TrackMethodSymbolAsync(IMethodSymbol methodSymbol, Doc } var node = location.FindNode(cancellationToken); - var sourceDoc = document.Project.Solution.GetRequiredDocument(location.SourceTree); + var sourceDoc = collector.Solution.GetRequiredDocument(location.SourceTree); var syntaxFacts = sourceDoc.GetRequiredLanguageService(); var returnStatements = node.DescendantNodesAndSelf().Where(n => syntaxFacts.IsReturnStatement(n)).ToImmutableArray(); var semanticModel = await sourceDoc.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); @@ -208,7 +209,7 @@ private static async Task TrackMethodSymbolAsync(IMethodSymbol methodSymbol, Doc continue; } - await TrackExpressionAsync(operation, sourceDoc, progressCollector, cancellationToken).ConfigureAwait(false); + await collector.VisitAsync(operation, cancellationToken).ConfigureAwait(false); } else { @@ -226,7 +227,7 @@ private static async Task TrackMethodSymbolAsync(IMethodSymbol methodSymbol, Doc continue; } - await TrackExpressionAsync(operation, sourceDoc, progressCollector, cancellationToken).ConfigureAwait(false); + await collector.VisitAsync(operation, cancellationToken).ConfigureAwait(false); } } } @@ -241,7 +242,7 @@ private static async Task TrackMethodSymbolAsync(IMethodSymbol methodSymbol, Doc continue; } - await TrackVariableSymbolAsync(outOrRefParam, document, progressCollector, cancellationToken).ConfigureAwait(false); + await TrackVariableSymbolAsync(outOrRefParam, collector, cancellationToken).ConfigureAwait(false); } } @@ -253,21 +254,6 @@ static bool HasAnOutOrRefParam(IMethodSymbol methodSymbol) => methodSymbol.Parameters.Any(p => p.IsRefOrOut()); } - private static async Task TrackExpressionAsync(IOperation expressionOperation, Document document, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) - { - if (expressionOperation.Children.Any()) - { - foreach (var childOperation in expressionOperation.Children) - { - await AddOperationAsync(childOperation, document, progressCollector, cancellationToken).ConfigureAwait(false); - } - } - else - { - await AddOperationAsync(expressionOperation, document, progressCollector, cancellationToken).ConfigureAwait(false); - } - } - private static async Task<(ISymbol?, SyntaxNode?)> GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); @@ -281,56 +267,7 @@ private static async Task TrackExpressionAsync(IOperation expressionOperation, D return (selectedSymbol, selectedNode); } - private static async Task AddOperationAsync(IOperation operation, Document document, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) - { - var semanticModel = operation.SemanticModel; - if (semanticModel is null) - { - return; - } - - var symbolInfo = semanticModel.GetSymbolInfo(operation.Syntax, cancellationToken); - if (symbolInfo.Symbol is null) - { - if (operation is ILiteralOperation { Type: not null } literalOperation) - { - await progressCollector.TryReportAsync(document.Project.Solution, - operation.Syntax.GetLocation(), - literalOperation.Type!, - cancellationToken: cancellationToken).ConfigureAwait(false); - } - - return; - } - - if (operation is IInvocationOperation invocationOperation) - { - // If the operation is an invocation, we want to find if invocations are part of the arguments as well - // and make sure they are added - foreach (var argument in invocationOperation.Arguments) - { - if (argument.Value is IInvocationOperation argumentInvocationOperation) - { - await AddOperationAsync(argumentInvocationOperation, document, progressCollector, cancellationToken).ConfigureAwait(false); - } - } - } - else if (operation is IReturnOperation { ReturnedValue: not null } returnOperation) - { - // For return operations we want to track - // the value returned in case it has invocations - // or other items that need to be handled special - await AddOperationAsync(returnOperation.ReturnedValue, document, progressCollector, cancellationToken).ConfigureAwait(false); - return; - } - - await progressCollector.TryReportAsync(document.Project.Solution, - operation.Syntax.GetLocation(), - symbolInfo.Symbol, - cancellationToken: cancellationToken).ConfigureAwait(false); - } - - private static async Task AddItemsFromAssignmentAsync(Document document, SyntaxNode lhsNode, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) + private static async Task AddItemsFromAssignmentAsync(Document document, SyntaxNode lhsNode, OperationCollector collector, CancellationToken cancellationToken) { var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var operation = semanticModel.GetOperation(lhsNode); @@ -354,36 +291,7 @@ private static async Task AddItemsFromAssignmentAsync(Document document, SyntaxN } var rhsOperation = assignmentOperation.Value; - await TrackExpressionAsync(rhsOperation, document, progressCollector, cancellationToken).ConfigureAwait(false); - } - - private static async Task TrackArgumentsAsync(ImmutableArray argumentOperations, Document document, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) - { - var collectorsAndArgumentMap = argumentOperations - .Select(argument => (collector: CreateCollector(), argument)) - .ToImmutableArray(); - - var tasks = collectorsAndArgumentMap - .Select(pair => TrackExpressionAsync(pair.argument.Value, document, pair.collector, cancellationToken)); - - await Task.WhenAll(tasks).ConfigureAwait(false); - - var items = collectorsAndArgumentMap - .Select(pair => pair.collector) - .SelectMany(collector => collector.GetItems()) - .Reverse(); // ProgressCollector uses a Stack, and we want to maintain the order by arguments, so reverse - - foreach (var item in items) - { - progressCollector.Report(item); - } - - ValueTrackingProgressCollector CreateCollector() - { - var collector = new ValueTrackingProgressCollector(); - collector.Parent = progressCollector.Parent; - return collector; - } + await collector.VisitAsync(rhsOperation, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs index 379ed13a696d5..59cadcf663697 100644 --- a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Linq; using System.Collections.Immutable; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ValueTracking; @@ -32,25 +33,10 @@ internal static async Task> GetTrackedItemsAsyn return await service.TrackValueSourceAsync(item, cancellationToken); } - internal static async Task> ValidateChildrenAsync(TestWorkspace testWorkspace, ValueTrackedItem item, int[] lines, CancellationToken cancellationToken = default) - { - var children = await GetTrackedItemsAsync(testWorkspace, item, cancellationToken); - - Assert.Equal(lines.Length, children.Length); - - for (var i = 0; i < lines.Length; i++) - { - ValidateItem(children[i], lines[i]); - } - - return children; - } - internal static async Task> ValidateChildrenAsync(TestWorkspace testWorkspace, ValueTrackedItem item, (int line, string text)[] childInfo, CancellationToken cancellationToken = default) { var children = await GetTrackedItemsAsync(testWorkspace, item, cancellationToken); - - Assert.Equal(childInfo.Length, children.Length); + Assert.True(childInfo.Length == children.Length, $"GetTrackedItemsAsync on [{item}]\n\texpected: [{string.Join(",", childInfo.Select(p => p.text))}]\n\t actual: [{string.Join(",", children)}]"); for (var i = 0; i < childInfo.Length; i++) { diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs index 3eaa99f0b9da0..8de6b220ba337 100644 --- a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -233,7 +233,8 @@ private string CalculateDefault(C c) // // S = s; [Code.cs:7] // |> S = [|s|] [Code.cs:7] - // |> c.SetS(s); [Code.cs:17] + // |> [|c.SetS(s)|]; [Code.cs:17] + // |> c.SetS([|s|]); [Code.cs:17] // |> CallS([|c|], CalculateDefault(c)) [Code.cs:22] // |> CallS(c, [|CalculateDefault(c)|]) [Code.cs:22] // |> return "" [Code.cs:37] @@ -246,27 +247,38 @@ private string CalculateDefault(C c) var items = await ValidateChildrenAsync( workspace, initialItems.Single(), - lines: new[] + childInfo: new[] { - 17 // |> c.SetS(s); [Code.cs:17] + (17, "s"), // |> c.SetS([|s|]); [Code.cs:17] + (17, "c.SetS(s)"), // |> [|c.SetS(s)|]; [Code.cs:17] }); + // |> [|c.SetS(s)|]; [Code.cs:17] + await ValidateChildrenEmptyAsync(workspace, items[1]); + + // |> c.SetS([|s|]); [Code.cs:17] items = await ValidateChildrenAsync( workspace, - items.Single(), + items[0], childInfo: new[] { (22, "c" ), // |> CallS([|c|], CalculateDefault(c)) [Code.cs:22] - (22, "CalculateDefault" ) // |> CallS(c, [|CalculateDefault(c)|]) [Code.cs:22] + (22, "c" ), // |> CallS(c, CalculateDefault([|c|])) [Code.cs:22] + (22, "CalculateDefault(c)" ), // |> CallS(c, [|CalculateDefault(c)|]) [Code.cs:22] + (22, "CallS(c, CalculateDefault(c))" ) // |> [|CallS(c, CalculateDefault(c))|] [Code.cs:22] }); - var referenceC = items[0]; - await ValidateChildrenEmptyAsync(workspace, referenceC); + // |> CallS([|c|], CalculateDefault(c)) [Code.cs:22] + await ValidateChildrenEmptyAsync(workspace, items[0]); + // |> CallS(c, CalculateDefault([|c|])) [Code.cs:22] + await ValidateChildrenEmptyAsync(workspace, items[1]); + // |> CallS(c, [|CalculateDefault(c)|]) [Code.cs:22] + await ValidateChildrenEmptyAsync(workspace, items[3]); - var calculateDefaultCall = items[1]; + // |> CallS(c, [|CalculateDefault(c)|]) [Code.cs:22] var children = await ValidateChildrenAsync( workspace, - calculateDefaultCall, + items[2], childInfo: new[] { (37, "\"\""), // |> return "" [Code.cs:37] @@ -347,53 +359,75 @@ public static void Main(string[] args) // // S = s; [Code.cs:7] // |> S = [|s|] [Code.cs:7] - // |> c.SetS(s); [Code.cs:23] + // |> [|c.SetS(s)|]; [Code.cs:23] + // |> c.SetS([|s|]); [Code.cs:23] // |> CallS([|c|], CalculateDefault(c) + _adornment) [Code.cs:28] // |> other.CallS([|c|]); [Code.cs:53] // |> CallS(c, CalculateDefault(c) + [|_adornment|]) [Code.cs:28] // |> _adornment = [|adornment|]; [Code.cs:18] // |> var other = new Other([|"some value"|]); [Code.cs:51] + // |> CallS(c, CalculateDefault([|c|]) + _adornment) [Code.cs:28] + // |> other.CallS([|c|]); [Code.cs:53] // |> CallS(c, [|CalculateDefault(c)|] + _adornment) [Code.cs:28] // |> return "" [Code.cs:37] // |> return "defaultstring" [Code.cs:34] // |> return "null" [Code.cs:29] - // + // |> [|CallS(c, CalculateDefault(c) + _adornment)|] [Code.cs:28] + // Assert.Equal(1, initialItems.Length); ValidateItem(initialItems[0], 7); var items = await ValidateChildrenAsync( workspace, initialItems.Single(), - lines: new[] + childInfo: new[] { - 23 // |> c.SetS(s); [Code.cs:23] + (23, "s"), // |> c.SetS([|s|]); [Code.cs:23] + (23, "c.SetS(s)"), // |> c.SetS(s); [Code.cs:23] }); + // |> c.SetS(s); [Code.cs:23] + await ValidateChildrenEmptyAsync(workspace, items[1]); + + // |> c.SetS([|s|]); [Code.cs:23] items = await ValidateChildrenAsync( workspace, - items.Single(), + items[0], childInfo: new[] { (28, "c" ), // |> CallS([|c|], CalculateDefault(c) + _adornment) [Code.cs:28] (28, "_adornment" ), // |> CallS(c, CalculateDefault(c) + [|_adornment|]) [Code.cs:28] + (28, "c" ), // |> CallS(c, CalculateDefault([|c|]) + _adornment) [Code.cs:28] (28, "CalculateDefault(c)" ), // |> CallS(c, [|CalculateDefault|](c) + _adornment) [Code.cs:28] + (28, "CallS(c, CalculateDefault(c) + _adornment)" ), // |> [|CallS(c, CalculateDefault(c) + _adornment)|] [Code.cs:28] }); - var referenceC = items[0]; + // |> CallS([|c|], CalculateDefault(c) + _adornment) [Code.cs:28] var children = await ValidateChildrenAsync( workspace, - referenceC, + items[0], + childInfo: new[] + { + (53, "other.CallS(c)"), // |> other.CallS([|c|]); [Code.cs:53] + }); + + await ValidateChildrenEmptyAsync(workspace, children); + + // |> CallS(c, CalculateDefault([|c|]) + _adornment) [Code.cs:28] + children = await ValidateChildrenAsync( + workspace, + items[2], childInfo: new[] { - (53, "c"), // |> other.CallS([|c|]); [Code.cs:53] + (53, "other.CallS(c)"), // |> other.CallS([|c|]); [Code.cs:53] }); await ValidateChildrenEmptyAsync(workspace, children); - var adornmentAssignment = items[1]; + // |> CallS(c, CalculateDefault(c) + [|_adornment|]) [Code.cs:28] children = await ValidateChildrenAsync( workspace, - adornmentAssignment, + items[1], childInfo: new[] { (18, "adornment"), // |> _adornment = [|adornment|] [Code.cs:18] @@ -408,10 +442,11 @@ public static void Main(string[] args) }); await ValidateChildrenEmptyAsync(workspace, children); - var calculateDefaultCall = items[2]; + + // |> CallS(c, [|CalculateDefault(c)|] + _adornment) [Code.cs:28] children = await ValidateChildrenAsync( workspace, - calculateDefaultCall, + items[3], childInfo: new[] { (43, "\"\""), // |> return "" [Code.cs:37] @@ -420,10 +455,13 @@ public static void Main(string[] args) }); await ValidateChildrenEmptyAsync(workspace, children); + + // |> [|CallS(c, CalculateDefault(c) + _adornment)|] [Code.cs:28] + await ValidateChildrenEmptyAsync(workspace, items[4]); } [Fact] - public async Task TestMethodTracking3() + public async Task MethodTracking3() { var code = @" @@ -450,6 +488,8 @@ public async Task Double(int x) }"; // // |> return [|x|] [Code.cs:18] + // |> x = await AddAsync([|x|], x) [Code.cs:17] + // |> x = await AddAsync(x, [|x|]) [Code.cs:17] // |> x = await [|AddAsync(x, x)|] [Code.cs:17] // |> [|Task.FromResult|](Add(x, y)) [Code.cs:13] // |> Task.FromResult([|Add(x, y)|]) [Code.cs:13] @@ -464,23 +504,30 @@ public async Task Double(int x) initialItems.Single(), childInfo: new[] { + (17, "x"), // |> x = await AddAsync([|x|], x) [Code.cs:17] + (17, "x"), // |> x = await AddAsync(x, [|x|]) [Code.cs:17] (17, "AddAsync(x, x)") // |> x = await [|AddAsync(x, x)|] [Code.cs:17] }); + // |> x = await [|AddAsync(x, x)|] [Code.cs:17] children = await ValidateChildrenAsync( workspace, - children.Single(), + children[2], childInfo: new[] { + (13, "x"), // |> Task.FromResult(Add([|x|], y)) [Code.cs:13] + (13, "y"), // |> Task.FromResult(Add(x, [|y|])) [Code.cs:13] + (13, "Add(x,y)"), // |> Task.FromResult([|Add(x, y)|]) [Code.cs:13] (13, "Task.FromResult(Add(x,y))"), // |> [|Task.FromResult|](Add(x, y)) [Code.cs:13] - (13, "Add(x,y)") // |> Task.FromResult([|Add(x, y)|]) [Code.cs:13] }); - await ValidateChildrenEmptyAsync(workspace, children[0]); + // |> [|Task.FromResult|](Add(x, y)) [Code.cs:13] + await ValidateChildrenEmptyAsync(workspace, children[3]); + // |> Task.FromResult([|Add(x, y)|]) [Code.cs:13] children = await ValidateChildrenAsync( workspace, - children[1], + children[2], childInfo: new[] { (10, "x") // |> return x [Code.cs:10] From 482a73562e460f1ca1cb482822c365ebaa797a5b Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Thu, 1 Apr 2021 16:28:59 -0700 Subject: [PATCH 027/127] Add better support for out params --- ...eTrackingService.FindReferencesProgress.cs | 17 ++- .../ValueTrackingService.Visitor.cs | 111 +++++++++++++++--- .../ValueTracking/ValueTrackingService.cs | 70 ++++++++--- .../AbstractBaseValueTrackingTests.cs | 13 ++ .../ValueTracking/CSharpValueTrackingTests.cs | 80 ++++++++++++- 5 files changed, 245 insertions(+), 46 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs index 0fadea6a379d9..0b83d311d46bb 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs @@ -2,11 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Reflection.Metadata; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindSymbols; @@ -14,7 +9,6 @@ using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; -using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.ValueTracking { @@ -51,8 +45,6 @@ public async ValueTask OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation l return; } - var solution = location.Document.Project.Solution; - if (symbol is IMethodSymbol methodSymbol) { if (methodSymbol.IsConstructor()) @@ -77,7 +69,14 @@ public async ValueTask OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation l } else { - await _operationCollector.ProgressCollector.TryReportAsync(solution, location.Location, symbol, _cancellationToken).ConfigureAwait(false); + var semanticModel = await location.Document.GetRequiredSemanticModelAsync(_cancellationToken).ConfigureAwait(false); + var operation = semanticModel.GetOperation(node, _cancellationToken); + if (operation is null) + { + return; + } + + await _operationCollector.VisitAsync(operation, _cancellationToken).ConfigureAwait(false); } } } diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs index 92749788861fa..59d8f91965ceb 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs @@ -2,14 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Linq; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Extensions; -using static Microsoft.CodeAnalysis.EditAndContinue.TraceLog; -using Microsoft.CodeAnalysis.Editor.Implementation.CommentSelection; namespace Microsoft.CodeAnalysis.ValueTracking { @@ -33,7 +32,14 @@ public Task VisitAsync(IOperation operation, CancellationToken cancellationToken IInvocationOperation invocationOperation => VisitInvocationAsync(invocationOperation, cancellationToken), ILiteralOperation literalOperation => VisitLiteralAsync(literalOperation, cancellationToken), IReturnOperation returnOperation => VisitReturnAsync(returnOperation, cancellationToken), + IArgumentOperation argumentOperation => ShouldTrackArgument(argumentOperation) ? VisitAsync(argumentOperation.Value, cancellationToken) : Task.CompletedTask, + ILocalReferenceOperation localReferenceOperation => VisitLocalReferenceAsync(localReferenceOperation, cancellationToken), + IParameterReferenceOperation parameterReferenceOperation => VisitParameterReferenceAsync(parameterReferenceOperation, cancellationToken), IFieldReferenceOperation fieldReferenceOperation => VisitFieldReferenceAsync(fieldReferenceOperation, cancellationToken), + IPropertyReferenceOperation propertyReferenceOperation => VisitPropertyReferenceAsync(propertyReferenceOperation, cancellationToken), + IAssignmentOperation assignmentOperation => VisitAssignmentOperationAsync(assignmentOperation, cancellationToken), + + // Default to reporting if there is symbol information available _ => VisitDefaultAsync(operation, cancellationToken) }; @@ -66,8 +72,8 @@ private async Task VisitDefaultAsync(IOperation operation, CancellationToken can await AddOperationAsync(operation, symbolInfo.Symbol, cancellationToken).ConfigureAwait(false); } - private Task VisitFieldReferenceAsync(IFieldReferenceOperation fieldReferenceOperation, CancellationToken cancellationToken) - => AddOperationAsync(fieldReferenceOperation, fieldReferenceOperation.Member, cancellationToken); + private Task VisitAssignmentOperationAsync(IAssignmentOperation assignmentOperation, CancellationToken cancellationToken) + => VisitDefaultAsync(assignmentOperation.Value, cancellationToken); private Task VisitObjectCreationAsync(IObjectCreationOperation objectCreationOperation, CancellationToken cancellationToken) => TrackArgumentsAsync(objectCreationOperation.Arguments, cancellationToken); @@ -78,6 +84,77 @@ private async Task VisitInvocationAsync(IInvocationOperation invocationOperation await TrackArgumentsAsync(invocationOperation.Arguments, cancellationToken).ConfigureAwait(false); } + private Task VisitLocalReferenceAsync(ILocalReferenceOperation localReferenceOperation, CancellationToken cancellationToken) + { + if (IsOutOrRefForMethod(localReferenceOperation, out var parameterSymbol)) + { + return AddOperationAsync(localReferenceOperation, parameterSymbol, cancellationToken); + } + + return Task.CompletedTask; + } + + private Task VisitParameterReferenceAsync(IParameterReferenceOperation parameterReferenceOperation, CancellationToken cancellationToken) + { + if (IsOutOrRefForMethod(parameterReferenceOperation, out var parameterSymbol)) + { + return AddOperationAsync(parameterReferenceOperation, parameterSymbol, cancellationToken); + } + + return Task.CompletedTask; + } + + private Task VisitFieldReferenceAsync(IFieldReferenceOperation fieldReferenceOperation, CancellationToken cancellationToken) + { + if (IsOutOrRefForMethod(fieldReferenceOperation, out var parameterSymbol)) + { + return AddOperationAsync(fieldReferenceOperation, parameterSymbol, cancellationToken); + } + + return Task.CompletedTask; + } + + private Task VisitPropertyReferenceAsync(IPropertyReferenceOperation propertyReferenceOperation, CancellationToken cancellationToken) + { + if (IsOutOrRefForMethod(propertyReferenceOperation, out var parameterSymbol)) + { + return AddOperationAsync(propertyReferenceOperation, parameterSymbol, cancellationToken); + } + + return Task.CompletedTask; + } + + private static bool IsOutOrRefForMethod(IOperation operation, [NotNullWhen(returnValue: true)] out IParameterSymbol? parameterSymbol) + { + var originalOperation = operation; + parameterSymbol = null; + + var argumentOperation = operation as IArgumentOperation; + while (argumentOperation is null) + { + if (operation.Parent is null) + { + return false; + } + + operation = operation.Parent; + argumentOperation = operation as IArgumentOperation; + } + + if (argumentOperation is null) + { + return false; + } + + if (argumentOperation.Value == originalOperation) + { + parameterSymbol = argumentOperation.Parameter; + return true; + } + + return false; + } + private async Task VisitLiteralAsync(ILiteralOperation literalOperation, CancellationToken cancellationToken) { if (literalOperation.Type is null) @@ -110,12 +187,12 @@ private async Task AddOperationAsync(IOperation operation, ISymbol symbol, Cance private async Task TrackArgumentsAsync(ImmutableArray argumentOperations, CancellationToken cancellationToken) { var collectorsAndArgumentMap = argumentOperations - .Where(ShouldTrack) + .Where(ShouldTrackArgument) .Select(argument => (collector: Clone(), argument)) .ToImmutableArray(); var tasks = collectorsAndArgumentMap - .Select(pair => pair.collector.VisitAsync(pair.argument.Value, cancellationToken)); + .Select(pair => pair.collector.VisitAsync(pair.argument, cancellationToken)); await Task.WhenAll(tasks).ConfigureAwait(false); @@ -128,16 +205,6 @@ private async Task TrackArgumentsAsync(ImmutableArray argume { ProgressCollector.Report(item); } - - static bool ShouldTrack(IArgumentOperation argumentOperation) - { - return argumentOperation.Parameter.IsRefOrOut() - || argumentOperation.Value is IExpressionStatementOperation - or IBinaryOperation - or IInvocationOperation - or IParameterReferenceOperation - or ILiteralOperation; - } } private OperationCollector Clone() @@ -146,6 +213,16 @@ private OperationCollector Clone() collector.Parent = ProgressCollector.Parent; return new OperationCollector(collector, Solution); } + + private static bool ShouldTrackArgument(IArgumentOperation argumentOperation) + { + return argumentOperation.Parameter?.IsRefOrOut() == true + || argumentOperation.Value is IExpressionStatementOperation + or IBinaryOperation + or IInvocationOperation + or IParameterReferenceOperation + or ILiteralOperation; + } } } } diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 8f436ec9d2d4e..3677971668fd0 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -5,28 +5,27 @@ using System; using System.Collections.Immutable; using System.Composition; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Reflection.Metadata; using System.Threading; using System.Threading.Tasks; -using System.Xml.Linq; -using Microsoft.CodeAnalysis.Elfie.Model; +using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.FindSymbols; -using Microsoft.CodeAnalysis.FlowAnalysis; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ValueTracking { + internal class BaseTrackingService + { + + } + [ExportWorkspaceService(typeof(IValueTrackingService)), Shared] - internal partial class ValueTrackingService : IValueTrackingService + internal partial class ValueTrackingService : BaseTrackingService, IValueTrackingService { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -51,6 +50,7 @@ public async Task TrackValueSourceAsync( CancellationToken cancellationToken) { var (symbol, node) = await GetSelectedSymbolAsync(selection, document, cancellationToken).ConfigureAwait(false); + var operationCollector = new OperationCollector(progressCollector, document.Project.Solution); if (symbol is IPropertySymbol @@ -59,7 +59,6 @@ or ILocalSymbol or IParameterSymbol) { RoslynDebug.AssertNotNull(node); - var operationCollector = new OperationCollector(progressCollector, document.Project.Solution); var solution = document.Project.Solution; var declaringSyntaxReferences = symbol.DeclaringSyntaxReferences; @@ -96,6 +95,20 @@ or ILocalSymbol await progressCollector.TryReportAsync(document.Project.Solution, node.GetLocation(), symbol, cancellationToken).ConfigureAwait(false); } } + else if (node is not null) + { + // If the correct symbol couldn't be determined but a node is available, try to let + // the operation collector handle the operation and report as needed + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var operation = semanticModel.GetOperation(node, cancellationToken); + + if (operation is null) + { + return; + } + + await operationCollector.VisitAsync(operation, cancellationToken).ConfigureAwait(false); + } } public async Task> TrackValueSourceAsync( @@ -128,10 +141,22 @@ public async Task TrackValueSourceAsync( case IParameterSymbol parameterSymbol: { - // The "output" is method calls, so track where this method is invoked for the parameter as - // well as assignments inside the method. Both contribute to the final values - await TrackVariableSymbolAsync(previousTrackedItem.Symbol, operationCollector, cancellationToken).ConfigureAwait(false); - await TrackParameterSymbolAsync(parameterSymbol, operationCollector, cancellationToken).ConfigureAwait(false); + if (parameterSymbol.IsRefOrOut()) + { + // For Ref or Out parameters, they contribute data across method calls through assignments + // within the method. No need to track returns + // Ex: TryGetValue("mykey", out var [|v|]) + // [|v|] is the interesting part, we don't care what the method returns + await TrackVariableSymbolAsync(parameterSymbol, operationCollector, cancellationToken).ConfigureAwait(false); + + } + else + { + // The "output" is method calls, so track where this method is invoked for the parameter as + // well as assignments inside the method. Both contribute to the final values + await TrackVariableSymbolAsync(parameterSymbol, operationCollector, cancellationToken).ConfigureAwait(false); + await TrackParameterSymbolAsync(parameterSymbol, operationCollector, cancellationToken).ConfigureAwait(false); + } } break; @@ -264,6 +289,22 @@ static bool HasAnOutOrRefParam(IMethodSymbol methodSymbol) semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol ?? semanticModel.GetDeclaredSymbol(selectedNode, cancellationToken); + if (selectedSymbol is null) + { + var syntaxFacts = document.GetRequiredLanguageService(); + + // If the node is an argument it's possible that it's just + // an identifier in the expression. If so, then grab the symbol + // for that node instead of the argument. + // EX: MyMethodCall($$x, y) should get the identifier x and + // the symbol for that identifier + if (syntaxFacts.IsArgument(selectedNode)) + { + selectedNode = syntaxFacts.GetExpressionOfArgument(selectedNode); + selectedSymbol = semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol; + } + } + return (selectedSymbol, selectedNode); } @@ -290,8 +331,7 @@ private static async Task AddItemsFromAssignmentAsync(Document document, SyntaxN return; } - var rhsOperation = assignmentOperation.Value; - await collector.VisitAsync(rhsOperation, cancellationToken).ConfigureAwait(false); + await collector.VisitAsync(assignmentOperation, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs index 59cadcf663697..c6706f24902af 100644 --- a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs @@ -33,6 +33,19 @@ internal static async Task> GetTrackedItemsAsyn return await service.TrackValueSourceAsync(item, cancellationToken); } + internal static async Task> ValidateItemsAsync(TestWorkspace testWorkspace, (int line, string text)[] itemInfo, CancellationToken cancellationToken = default) + { + var items = await GetTrackedItemsAsync(testWorkspace, cancellationToken); + Assert.True(itemInfo.Length == items.Length, $"GetTrackedItemsAsync\n\texpected: [{string.Join(",", itemInfo.Select(p => p.text))}]\n\t actual: [{string.Join(",", items)}]"); + + for (var i = 0; i < items.Length; i++) + { + ValidateItem(items[i], itemInfo[i].line, itemInfo[i].text); + } + + return items; + } + internal static async Task> ValidateChildrenAsync(TestWorkspace testWorkspace, ValueTrackedItem item, (int line, string text)[] childInfo, CancellationToken cancellationToken = default) { var children = await GetTrackedItemsAsync(testWorkspace, item, cancellationToken); diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs index 8de6b220ba337..d141a2aad1965 100644 --- a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -7,6 +7,7 @@ using Xunit; using Microsoft.CodeAnalysis.Test.Utilities; using System.Linq; +using System; namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking { @@ -31,16 +32,19 @@ public void SetS(string s) } "; using var workspace = TestWorkspace.CreateCSharp(code); - var initialItems = await GetTrackedItemsAsync(workspace); // // property S // |> S = s [Code.cs:7] // |> public string S { get; set; } [Code.cs:3] // - Assert.Equal(2, initialItems.Length); - ValidateItem(initialItems[0], 7); - ValidateItem(initialItems[1], 3); + await ValidateItemsAsync( + workspace, + itemInfo: new[] + { + (7, "s"), + (3, "public string S { get; set; } = \"\""), + }); } [Fact] @@ -442,7 +446,6 @@ public static void Main(string[] args) }); await ValidateChildrenEmptyAsync(workspace, children); - // |> CallS(c, [|CalculateDefault(c)|] + _adornment) [Code.cs:28] children = await ValidateChildrenAsync( workspace, @@ -533,5 +536,72 @@ public async Task Double(int x) (10, "x") // |> return x [Code.cs:10] }); } + + [Fact] + public async Task OutParam() + { + var code = @" +class C +{ + bool TryConvertInt(object o, out int i) + { + if (int.TryParse(o.ToString(), out i)) + { + return true; + } + + return false; + } + + void M() + { + int i = 0; + object o = ""5""; + + if (TryConvertInt(o, out i)) + { + Console.WriteLine($$i); + } + else + { + i = 2; + } + } +}"; + + // + // |> Console.WriteLine($$i); [Code.cs:20] + // |> i = [|2|] [Code.cs:24] + // |> if (TryConvertInt(o, out [|i|])) [Code.cs:18] + // |> if (int.TryParse(o.ToString(), out [|i|])) [Code.cs:5] + using var workspace = TestWorkspace.CreateCSharp(code); + var initialItems = await GetTrackedItemsAsync(workspace); + + Assert.Equal(1, initialItems.Length); + + // |> Console.WriteLine($$i);[Code.cs:20] + ValidateItem(initialItems.Single(), 20, "i"); + + var children = await ValidateChildrenAsync( + workspace, + initialItems.Single(), + childInfo: new[] + { + (24, "2"), // |> i = [|2|] [Code.cs:24] + (18, "i"), // |> if (TryConvertInt(o, out [|i|])) [Code.cs:18] + }); + + await ValidateChildrenEmptyAsync(workspace, children[0]); + + children = await ValidateChildrenAsync( + workspace, + children[1], + childInfo: new[] + { + (5, "i") // |> if (int.TryParse(o.ToString(), out [|i|])) [Code.cs:5] + }); + + await ValidateChildrenEmptyAsync(workspace, children[0]); + } } } From 2298346b5a56c1e0af3b3209dcb05013af174e35 Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Fri, 2 Apr 2021 01:05:00 -0700 Subject: [PATCH 028/127] Fix tests --- .../ValueTrackingService.Visitor.cs | 98 ++++++++++--------- .../ValueTracking/ValueTrackingService.cs | 40 +++++--- .../ValueTracking/CSharpValueTrackingTests.cs | 19 ++-- 3 files changed, 84 insertions(+), 73 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs index 59d8f91965ceb..762117d4871da 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; @@ -33,10 +34,10 @@ public Task VisitAsync(IOperation operation, CancellationToken cancellationToken ILiteralOperation literalOperation => VisitLiteralAsync(literalOperation, cancellationToken), IReturnOperation returnOperation => VisitReturnAsync(returnOperation, cancellationToken), IArgumentOperation argumentOperation => ShouldTrackArgument(argumentOperation) ? VisitAsync(argumentOperation.Value, cancellationToken) : Task.CompletedTask, - ILocalReferenceOperation localReferenceOperation => VisitLocalReferenceAsync(localReferenceOperation, cancellationToken), - IParameterReferenceOperation parameterReferenceOperation => VisitParameterReferenceAsync(parameterReferenceOperation, cancellationToken), - IFieldReferenceOperation fieldReferenceOperation => VisitFieldReferenceAsync(fieldReferenceOperation, cancellationToken), - IPropertyReferenceOperation propertyReferenceOperation => VisitPropertyReferenceAsync(propertyReferenceOperation, cancellationToken), + ILocalReferenceOperation or + IParameterReferenceOperation or + IFieldReferenceOperation or + IPropertyReferenceOperation => VisitReferenceAsync(operation, cancellationToken), IAssignmentOperation assignmentOperation => VisitAssignmentOperationAsync(assignmentOperation, cancellationToken), // Default to reporting if there is symbol information available @@ -84,72 +85,75 @@ private async Task VisitInvocationAsync(IInvocationOperation invocationOperation await TrackArgumentsAsync(invocationOperation.Arguments, cancellationToken).ConfigureAwait(false); } - private Task VisitLocalReferenceAsync(ILocalReferenceOperation localReferenceOperation, CancellationToken cancellationToken) + private Task VisitReferenceAsync(IOperation operation, CancellationToken cancellationToken) { - if (IsOutOrRefForMethod(localReferenceOperation, out var parameterSymbol)) - { - return AddOperationAsync(localReferenceOperation, parameterSymbol, cancellationToken); - } + Debug.Assert(operation is + ILocalReferenceOperation or + IParameterReferenceOperation or + IFieldReferenceOperation or + IPropertyReferenceOperation); - return Task.CompletedTask; - } - - private Task VisitParameterReferenceAsync(IParameterReferenceOperation parameterReferenceOperation, CancellationToken cancellationToken) - { - if (IsOutOrRefForMethod(parameterReferenceOperation, out var parameterSymbol)) + if (IsArgument(operation, out var argumentOperation) && argumentOperation.Parameter is not null) { - return AddOperationAsync(parameterReferenceOperation, parameterSymbol, cancellationToken); - } + if (argumentOperation.Parameter.IsRefOrOut()) + { + // Always add ref or out parameters to track as assignments since the values count as + // assignments across method calls for the purposes of value tracking. + return AddOperationAsync(operation, argumentOperation.Parameter, cancellationToken); + } - return Task.CompletedTask; - } + // If the parameter is not a ref or out param, track the reference assignments that count + // as input to the argument being passed to the method. + return AddReference(operation, cancellationToken); + } - private Task VisitFieldReferenceAsync(IFieldReferenceOperation fieldReferenceOperation, CancellationToken cancellationToken) - { - if (IsOutOrRefForMethod(fieldReferenceOperation, out var parameterSymbol)) + if (IsReturn(operation)) { - return AddOperationAsync(fieldReferenceOperation, parameterSymbol, cancellationToken); + // If the reference is part of a return operation we want to track where the values come from + // since they contribute to the "output" of the method and are relavent for value tracking. + return AddReference(operation, cancellationToken); } return Task.CompletedTask; + + Task AddReference(IOperation operation, CancellationToken cancellationToken) + => operation switch + { + IParameterReferenceOperation parameterReference => AddOperationAsync(operation, parameterReference.Parameter, cancellationToken), + IFieldReferenceOperation fieldReferenceOperation => AddOperationAsync(operation, fieldReferenceOperation.Member, cancellationToken), + IPropertyReferenceOperation propertyReferenceOperation => AddOperationAsync(operation, propertyReferenceOperation.Member, cancellationToken), + ILocalReferenceOperation localReferenceOperation => AddOperationAsync(operation, localReferenceOperation.Local, cancellationToken), + _ => Task.CompletedTask + }; } - private Task VisitPropertyReferenceAsync(IPropertyReferenceOperation propertyReferenceOperation, CancellationToken cancellationToken) + private static bool IsArgument(IOperation? operation, [NotNullWhen(returnValue: true)] out IArgumentOperation? argumentOperation) { - if (IsOutOrRefForMethod(propertyReferenceOperation, out var parameterSymbol)) + while (operation is not null) { - return AddOperationAsync(propertyReferenceOperation, parameterSymbol, cancellationToken); + if (operation is IArgumentOperation tmpArgumentOperation) + { + argumentOperation = tmpArgumentOperation; + return true; + } + + operation = operation.Parent; } - return Task.CompletedTask; + argumentOperation = null; + return false; } - private static bool IsOutOrRefForMethod(IOperation operation, [NotNullWhen(returnValue: true)] out IParameterSymbol? parameterSymbol) + private static bool IsReturn(IOperation? operation) { - var originalOperation = operation; - parameterSymbol = null; - - var argumentOperation = operation as IArgumentOperation; - while (argumentOperation is null) + while (operation is not null) { - if (operation.Parent is null) + if (operation is IReturnOperation) { - return false; + return true; } operation = operation.Parent; - argumentOperation = operation as IArgumentOperation; - } - - if (argumentOperation is null) - { - return false; - } - - if (argumentOperation.Value == originalOperation) - { - parameterSymbol = argumentOperation.Parameter; - return true; } return false; diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 3677971668fd0..32e020ee5b4c8 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -95,7 +95,7 @@ or ILocalSymbol await progressCollector.TryReportAsync(document.Project.Solution, node.GetLocation(), symbol, cancellationToken).ConfigureAwait(false); } } - else if (node is not null) + else if (node is not null && symbol is null) { // If the correct symbol couldn't be determined but a node is available, try to let // the operation collector handle the operation and report as needed @@ -141,20 +141,30 @@ public async Task TrackValueSourceAsync( case IParameterSymbol parameterSymbol: { - if (parameterSymbol.IsRefOrOut()) + var previousSymbol = previousTrackedItem.Parent?.Symbol; + + // If the current parameter is a parameter symbol for the previous tracked method it should be treated differently. + // For example: + // string PrependString(string pre, string s) => pre + s; + // ^--- previously tracked ^---- current parameter being tracked + // + // In this case, s is being tracked because it contributed to the return of the method. We only + // want to track assignments to s that could impact the return rather than tracking the same method + // twice. + var isParameterForPreviousTrackedMethod = previousSymbol == parameterSymbol.ContainingSymbol; + + // For Ref or Out parameters, they contribute data across method calls through assignments + // within the method. No need to track returns + // Ex: TryGetValue("mykey", out var [|v|]) + // [|v|] is the interesting part, we don't care what the method returns + var isRefOrOut = parameterSymbol.IsRefOrOut(); + + // Always track the parameter assignments as variables, in case they are assigned anywhere in the method + await TrackVariableSymbolAsync(parameterSymbol, operationCollector, cancellationToken).ConfigureAwait(false); + + var trackMethod = !(isParameterForPreviousTrackedMethod || isRefOrOut); + if (trackMethod) { - // For Ref or Out parameters, they contribute data across method calls through assignments - // within the method. No need to track returns - // Ex: TryGetValue("mykey", out var [|v|]) - // [|v|] is the interesting part, we don't care what the method returns - await TrackVariableSymbolAsync(parameterSymbol, operationCollector, cancellationToken).ConfigureAwait(false); - - } - else - { - // The "output" is method calls, so track where this method is invoked for the parameter as - // well as assignments inside the method. Both contribute to the final values - await TrackVariableSymbolAsync(parameterSymbol, operationCollector, cancellationToken).ConfigureAwait(false); await TrackParameterSymbolAsync(parameterSymbol, operationCollector, cancellationToken).ConfigureAwait(false); } } @@ -300,7 +310,7 @@ static bool HasAnOutOrRefParam(IMethodSymbol methodSymbol) // the symbol for that identifier if (syntaxFacts.IsArgument(selectedNode)) { - selectedNode = syntaxFacts.GetExpressionOfArgument(selectedNode); + selectedNode = syntaxFacts.GetExpressionOfArgument(selectedNode)!; selectedSymbol = semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol; } } diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs index d141a2aad1965..69231e895ef15 100644 --- a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -205,9 +205,9 @@ public void SetS(string s) class Other { - public void CallS(C c, string s) + public void CallS(C c, string str) { - c.SetS(s); + c.SetS(str); } public void CallS(C c) @@ -237,8 +237,8 @@ private string CalculateDefault(C c) // // S = s; [Code.cs:7] // |> S = [|s|] [Code.cs:7] - // |> [|c.SetS(s)|]; [Code.cs:17] - // |> c.SetS([|s|]); [Code.cs:17] + // |> [|c.SetS(str)|]; [Code.cs:17] + // |> c.SetS([|str|]); [Code.cs:17] // |> CallS([|c|], CalculateDefault(c)) [Code.cs:22] // |> CallS(c, [|CalculateDefault(c)|]) [Code.cs:22] // |> return "" [Code.cs:37] @@ -253,8 +253,8 @@ private string CalculateDefault(C c) initialItems.Single(), childInfo: new[] { - (17, "s"), // |> c.SetS([|s|]); [Code.cs:17] - (17, "c.SetS(s)"), // |> [|c.SetS(s)|]; [Code.cs:17] + (17, "str"), // |> c.SetS([|str|]); [Code.cs:17] + (17, "c.SetS(str)"), // |> [|c.SetS(str)|]; [Code.cs:17] }); // |> [|c.SetS(s)|]; [Code.cs:17] @@ -587,21 +587,18 @@ void M() initialItems.Single(), childInfo: new[] { - (24, "2"), // |> i = [|2|] [Code.cs:24] (18, "i"), // |> if (TryConvertInt(o, out [|i|])) [Code.cs:18] }); - await ValidateChildrenEmptyAsync(workspace, children[0]); - children = await ValidateChildrenAsync( workspace, - children[1], + children.Single(), childInfo: new[] { (5, "i") // |> if (int.TryParse(o.ToString(), out [|i|])) [Code.cs:5] }); - await ValidateChildrenEmptyAsync(workspace, children[0]); + await ValidateChildrenEmptyAsync(workspace, children.Single()); } } } From cec3a6c01c1184d729e0812d7d6c8caf7ff9374d Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Fri, 2 Apr 2021 01:07:38 -0700 Subject: [PATCH 029/127] >.> --- .../Core/ValueTracking/ValueTrackingService.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 32e020ee5b4c8..c12f3ec1f0280 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServices; @@ -19,13 +18,8 @@ namespace Microsoft.CodeAnalysis.ValueTracking { - internal class BaseTrackingService - { - - } - [ExportWorkspaceService(typeof(IValueTrackingService)), Shared] - internal partial class ValueTrackingService : BaseTrackingService, IValueTrackingService + internal partial class ValueTrackingService : IValueTrackingService { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] From ff59bc04bb65168e384d820a7b6006cd0933f669 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Fri, 2 Apr 2021 08:10:54 -0700 Subject: [PATCH 030/127] Allow `abstract` and `sealed` modifiers on static methods/properties/events in interfaces. (#52228) Spec: https://github.com/dotnet/csharplang/blob/9a0dddbf0643993db0da8f48436f2aac60bc20b1/proposals/statics-in-interfaces.md Related to #52202. Relevant quotes from the spec: #### Abstract virtual members Static interface members other than fields are allowed to also have the `abstract` modifier. Abstract static members are not allowed to have a body (or in the case of properties, the accessors are not allowed to have a body). ``` c# interface I where T : I { static abstract void M(); static abstract T P { get; set; } static abstract event Action E; static abstract T operator +(T l, T r); } ``` #### Explicitly non-virtual static members For symmetry with non-virtual instance members, static members should be allowed an optional `sealed` modifier, even though they are non-virtual by default: ``` c# interface I0 { static sealed void M() => Console.WriteLine("Default behavior"); static sealed int f = 0; static sealed int P1 { get; set; } static sealed int P2 { get => f; set => f = value; } static sealed event Action E1; static sealed event Action E2 { add => E1 += value; remove => E1 -= value; } static sealed I0 operator +(I0 l, I0 r) => l; } ``` --- .../StaticAbstractMembersInInterfaces.md | 14 + .../CSharp/Portable/CSharpResources.resx | 5 +- .../CSharp/Portable/Errors/MessageID.cs | 2 + .../Portable/Symbols/Source/ModifierUtils.cs | 90 +- .../Symbols/Source/SourceEventSymbol.cs | 8 +- .../Source/SourceMemberMethodSymbol.cs | 5 +- .../Source/SourceOrdinaryMethodSymbolBase.cs | 8 +- .../Source/SourcePropertySymbolBase.cs | 8 +- .../SourceUserDefinedConversionSymbol.cs | 2 +- .../Source/SourceUserDefinedOperatorSymbol.cs | 2 +- .../SourceUserDefinedOperatorSymbolBase.cs | 71 +- .../Portable/xlf/CSharpResources.cs.xlf | 9 +- .../Portable/xlf/CSharpResources.de.xlf | 9 +- .../Portable/xlf/CSharpResources.es.xlf | 9 +- .../Portable/xlf/CSharpResources.fr.xlf | 9 +- .../Portable/xlf/CSharpResources.it.xlf | 9 +- .../Portable/xlf/CSharpResources.ja.xlf | 9 +- .../Portable/xlf/CSharpResources.ko.xlf | 9 +- .../Portable/xlf/CSharpResources.pl.xlf | 9 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 9 +- .../Portable/xlf/CSharpResources.ru.xlf | 9 +- .../Portable/xlf/CSharpResources.tr.xlf | 9 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 9 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 9 +- .../Test/Semantic/Semantics/BindingTests.cs | 5 +- .../DefaultInterfaceImplementationTests.cs | 176 +- .../StaticAbstractMembersInInterfacesTests.cs | 4145 +++++++++++++++++ .../Test/Symbol/Symbols/SymbolErrorTests.cs | 87 +- 28 files changed, 4533 insertions(+), 212 deletions(-) create mode 100644 docs/features/StaticAbstractMembersInInterfaces.md create mode 100644 src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs diff --git a/docs/features/StaticAbstractMembersInInterfaces.md b/docs/features/StaticAbstractMembersInInterfaces.md new file mode 100644 index 0000000000000..c2cec6b47ce3b --- /dev/null +++ b/docs/features/StaticAbstractMembersInInterfaces.md @@ -0,0 +1,14 @@ +Static Abstract Members In Interfaces +===================================== + +An interface is allowed to specify abstract static members that implementing classes and structs are then +required to provide an explicit or implicit implementation of. The members can be accessed off of type +parameters that are constrained by the interface. + +Proposal: +- https://github.com/dotnet/csharplang/issues/4436 +- https://github.com/dotnet/csharplang/blob/main/proposals/statics-in-interfaces.md + +Feature branch: https://github.com/dotnet/roslyn/tree/features/StaticAbstractMembersInInterfaces + +Test plan: https://github.com/dotnet/roslyn/issues/52221 \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 71a2cfd705946..82070fe3218ca 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -667,7 +667,7 @@ Type '{1}' already defines a member called '{0}' with the same parameter types - A static member '{0}' cannot be marked as override, virtual, or abstract + A static member cannot be marked as '{0}' A member '{0}' marked as override cannot be marked as new or virtual @@ -6600,4 +6600,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Using a function pointer type in a 'typeof' in an attribute is not supported. + + static abstract members in interfaces + diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index b9399edf14c78..ce8a982c0ef5f 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -216,6 +216,7 @@ internal enum MessageID IDS_FeatureVarianceSafetyForStaticInterfaceMembers = MessageBase + 12791, IDS_FeatureConstantInterpolatedStrings = MessageBase + 12792, IDS_FeatureMixedDeclarationsAndExpressionsInDeconstruction = MessageBase + 12793, + IDS_FeatureStaticAbstractMembersInInterfaces = MessageBase + 12794, } // Message IDs may refer to strings that need to be localized. @@ -324,6 +325,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) { // C# preview features. case MessageID.IDS_FeatureMixedDeclarationsAndExpressionsInDeconstruction: + case MessageID.IDS_FeatureStaticAbstractMembersInInterfaces: // semantic check return LanguageVersion.Preview; // C# 9.0 features. case MessageID.IDS_FeatureLambdaDiscardParameters: // semantic check diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs index fd97bfd16563c..4ed54d4228b21 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs @@ -39,6 +39,14 @@ internal static DeclarationModifiers CheckModifiers( out bool modifierErrors) { modifierErrors = false; + DeclarationModifiers reportStaticNotVirtualForModifiers = DeclarationModifiers.None; + + if ((modifiers & allowedModifiers & DeclarationModifiers.Static) != 0) + { + reportStaticNotVirtualForModifiers = allowedModifiers & (DeclarationModifiers.Override | DeclarationModifiers.Virtual); + allowedModifiers &= ~reportStaticNotVirtualForModifiers; + } + DeclarationModifiers errorModifiers = modifiers & ~allowedModifiers; DeclarationModifiers result = modifiers & allowedModifiers; @@ -55,6 +63,16 @@ internal static DeclarationModifiers CheckModifiers( ReportPartialError(errorLocation, diagnostics, modifierTokens); break; + case DeclarationModifiers.Override: + case DeclarationModifiers.Virtual: + if ((reportStaticNotVirtualForModifiers & oneError) == 0) + { + goto default; + } + + diagnostics.Add(ErrorCode.ERR_StaticNotVirtual, errorLocation, ModifierUtils.ConvertSingleModifierToSyntaxText(oneError)); + break; + default: diagnostics.Add(ErrorCode.ERR_BadMemberFlag, errorLocation, ConvertSingleModifierToSyntaxText(oneError)); break; @@ -94,27 +112,63 @@ internal static void ReportDefaultInterfaceImplementationModifiers( Location errorLocation, BindingDiagnosticBag diagnostics) { - if (!hasBody && (modifiers & defaultInterfaceImplementationModifiers) != 0) + if ((modifiers & defaultInterfaceImplementationModifiers) != 0) { LanguageVersion availableVersion = ((CSharpParseOptions)errorLocation.SourceTree.Options).LanguageVersion; - LanguageVersion requiredVersion = MessageID.IDS_DefaultInterfaceImplementation.RequiredVersion(); - if (availableVersion < requiredVersion) + LanguageVersion requiredVersion; + + if ((modifiers & defaultInterfaceImplementationModifiers & DeclarationModifiers.Static) != 0 && + (modifiers & defaultInterfaceImplementationModifiers & (DeclarationModifiers.Sealed | DeclarationModifiers.Abstract)) != 0) { - DeclarationModifiers errorModifiers = modifiers & defaultInterfaceImplementationModifiers; - var requiredVersionArgument = new CSharpRequiredLanguageVersion(requiredVersion); - var availableVersionArgument = availableVersion.ToDisplayString(); - while (errorModifiers != DeclarationModifiers.None) + var reportModifiers = DeclarationModifiers.Sealed | DeclarationModifiers.Abstract; + if ((modifiers & defaultInterfaceImplementationModifiers & (DeclarationModifiers.Sealed | DeclarationModifiers.Abstract)) == (DeclarationModifiers.Sealed | DeclarationModifiers.Abstract)) + { + diagnostics.Add(ErrorCode.ERR_BadMemberFlag, errorLocation, ConvertSingleModifierToSyntaxText(DeclarationModifiers.Sealed)); + reportModifiers &= ~DeclarationModifiers.Sealed; + } + + requiredVersion = MessageID.IDS_FeatureStaticAbstractMembersInInterfaces.RequiredVersion(); + if (availableVersion < requiredVersion) { - DeclarationModifiers oneError = errorModifiers & ~(errorModifiers - 1); - Debug.Assert(oneError != DeclarationModifiers.None); - errorModifiers = errorModifiers & ~oneError; - diagnostics.Add(ErrorCode.ERR_InvalidModifierForLanguageVersion, errorLocation, - ConvertSingleModifierToSyntaxText(oneError), - availableVersionArgument, - requiredVersionArgument); + report(modifiers, reportModifiers, errorLocation, diagnostics, availableVersion, requiredVersion); + } + + return; // below we will either ask for an earlier version of the language, or will not report anything + } + + if (hasBody) + { + if ((modifiers & defaultInterfaceImplementationModifiers & DeclarationModifiers.Static) != 0) + { + Binder.CheckFeatureAvailability(errorLocation.SourceTree, MessageID.IDS_DefaultInterfaceImplementation, diagnostics, errorLocation); + } + } + else + { + requiredVersion = MessageID.IDS_DefaultInterfaceImplementation.RequiredVersion(); + if (availableVersion < requiredVersion) + { + report(modifiers, defaultInterfaceImplementationModifiers, errorLocation, diagnostics, availableVersion, requiredVersion); } } } + + static void report(DeclarationModifiers modifiers, DeclarationModifiers unsupportedModifiers, Location errorLocation, BindingDiagnosticBag diagnostics, LanguageVersion availableVersion, LanguageVersion requiredVersion) + { + DeclarationModifiers errorModifiers = modifiers & unsupportedModifiers; + var requiredVersionArgument = new CSharpRequiredLanguageVersion(requiredVersion); + var availableVersionArgument = availableVersion.ToDisplayString(); + while (errorModifiers != DeclarationModifiers.None) + { + DeclarationModifiers oneError = errorModifiers & ~(errorModifiers - 1); + Debug.Assert(oneError != DeclarationModifiers.None); + errorModifiers = errorModifiers & ~oneError; + diagnostics.Add(ErrorCode.ERR_InvalidModifierForLanguageVersion, errorLocation, + ConvertSingleModifierToSyntaxText(oneError), + availableVersionArgument, + requiredVersionArgument); + } + } } internal static DeclarationModifiers AdjustModifiersForAnInterfaceMember(DeclarationModifiers mods, bool hasBody, bool isExplicitInterfaceImplementation) @@ -126,7 +180,11 @@ internal static DeclarationModifiers AdjustModifiersForAnInterfaceMember(Declara mods |= DeclarationModifiers.Sealed; } } - else if ((mods & (DeclarationModifiers.Static | DeclarationModifiers.Private | DeclarationModifiers.Partial | DeclarationModifiers.Virtual | DeclarationModifiers.Abstract)) == 0) + else if ((mods & DeclarationModifiers.Static) != 0) + { + mods &= ~DeclarationModifiers.Sealed; + } + else if ((mods & (DeclarationModifiers.Private | DeclarationModifiers.Partial | DeclarationModifiers.Virtual | DeclarationModifiers.Abstract)) == 0) { Debug.Assert(!isExplicitInterfaceImplementation); @@ -162,7 +220,7 @@ internal static DeclarationModifiers AdjustModifiersForAnInterfaceMember(Declara return mods; } - private static string ConvertSingleModifierToSyntaxText(DeclarationModifiers modifier) + internal static string ConvertSingleModifierToSyntaxText(DeclarationModifiers modifier) { switch (modifier) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs index 938424624c05f..e6e018667b044 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs @@ -504,6 +504,8 @@ private DeclarationModifiers MakeModifiers(SyntaxTokenList modifiers, bool expli protected void CheckModifiersAndType(BindingDiagnosticBag diagnostics) { + Debug.Assert(!IsStatic || (!IsVirtual && !IsOverride)); // Otherwise 'virtual' and 'override' should have been reported and cleared earlier. + Location location = this.Locations[0]; var useSiteInfo = new CompoundUseSiteInfo(diagnostics, ContainingAssembly); bool isExplicitInterfaceImplementationInInterface = ContainingType.IsInterface && IsExplicitInterfaceImplementation; @@ -512,10 +514,10 @@ protected void CheckModifiersAndType(BindingDiagnosticBag diagnostics) { diagnostics.Add(ErrorCode.ERR_VirtualPrivate, location, this); } - else if (IsStatic && (IsOverride || IsVirtual || IsAbstract)) + else if (IsStatic && IsAbstract && !ContainingType.IsInterface) { - // A static member '{0}' cannot be marked as override, virtual, or abstract - diagnostics.Add(ErrorCode.ERR_StaticNotVirtual, location, this); + // A static member '{0}' cannot be marked as 'abstract' + diagnostics.Add(ErrorCode.ERR_StaticNotVirtual, location, ModifierUtils.ConvertSingleModifierToSyntaxText(DeclarationModifiers.Abstract)); } else if (IsReadOnly && IsStatic) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs index 907bb7a56e9ab..2c9af64aa46a1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs @@ -969,7 +969,8 @@ protected void CheckFeatureAvailabilityAndRuntimeSupport(SyntaxNode declarationS { if (_containingType.IsInterface) { - if (hasBody || IsExplicitInterfaceImplementation) + if ((!IsStatic || MethodKind is MethodKind.StaticConstructor) && + (hasBody || IsExplicitInterfaceImplementation)) { Binder.CheckFeatureAvailability(declarationSyntax, MessageID.IDS_DefaultInterfaceImplementation, diagnostics, location); } @@ -978,6 +979,8 @@ protected void CheckFeatureAvailabilityAndRuntimeSupport(SyntaxNode declarationS { diagnostics.Add(ErrorCode.ERR_RuntimeDoesNotSupportDefaultInterfaceImplementation, location); } + + // PROTOTYPE(StaticAbstractMembersInInterfaces): Check runtime capability. } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs index 80f4b652af6fe..45091c65592df 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs @@ -498,6 +498,8 @@ private static DeclarationModifiers AddImpliedModifiers(DeclarationModifiers mod private void CheckModifiers(bool isExplicitInterfaceImplementation, bool isVararg, bool hasBody, Location location, BindingDiagnosticBag diagnostics) { + Debug.Assert(!IsStatic || (!IsVirtual && !IsOverride)); // Otherwise 'virtual' and 'override' should have been reported and cleared earlier. + bool isExplicitInterfaceImplementationInInterface = isExplicitInterfaceImplementation && ContainingType.IsInterface; if (IsPartial && HasExplicitAccessModifier) @@ -525,10 +527,10 @@ private void CheckModifiers(bool isExplicitInterfaceImplementation, bool isVarar { diagnostics.Add(ErrorCode.ERR_VirtualPrivate, location, this); } - else if (IsStatic && (IsOverride || IsVirtual || IsAbstract)) + else if (IsStatic && IsAbstract && !ContainingType.IsInterface) { - // A static member '{0}' cannot be marked as override, virtual, or abstract - diagnostics.Add(ErrorCode.ERR_StaticNotVirtual, location, this); + // A static member '{0}' cannot be marked as 'abstract' + diagnostics.Add(ErrorCode.ERR_StaticNotVirtual, location, ModifierUtils.ConvertSingleModifierToSyntaxText(DeclarationModifiers.Abstract)); } else if (IsOverride && (IsNew || IsVirtual)) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs index 4a51ec7d97743..d2b9a4e6592fb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs @@ -846,16 +846,18 @@ private void CheckAccessibility(Location location, BindingDiagnosticBag diagnost private void CheckModifiers(bool isExplicitInterfaceImplementation, Location location, bool isIndexer, BindingDiagnosticBag diagnostics) { + Debug.Assert(!IsStatic || (!IsVirtual && !IsOverride)); // Otherwise 'virtual' and 'override' should have been reported and cleared earlier. + bool isExplicitInterfaceImplementationInInterface = isExplicitInterfaceImplementation && ContainingType.IsInterface; if (this.DeclaredAccessibility == Accessibility.Private && (IsVirtual || (IsAbstract && !isExplicitInterfaceImplementationInInterface) || IsOverride)) { diagnostics.Add(ErrorCode.ERR_VirtualPrivate, location, this); } - else if (IsStatic && (IsOverride || IsVirtual || IsAbstract)) + else if (IsStatic && IsAbstract && !ContainingType.IsInterface) { - // A static member '{0}' cannot be marked as override, virtual, or abstract - diagnostics.Add(ErrorCode.ERR_StaticNotVirtual, location, this); + // A static member '{0}' cannot be marked as 'abstract' + diagnostics.Add(ErrorCode.ERR_StaticNotVirtual, location, ModifierUtils.ConvertSingleModifierToSyntaxText(DeclarationModifiers.Abstract)); } else if (IsStatic && HasReadOnlyModifier) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs index 070b65c225096..937bd94ce0f7a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs @@ -46,7 +46,7 @@ private SourceUserDefinedConversionSymbol( containingType, location, syntax, - MakeDeclarationModifiers(syntax, location, diagnostics), + MakeDeclarationModifiers(containingType.IsInterface, syntax, location, diagnostics), hasBody: syntax.HasAnyBody(), isExpressionBodied: syntax.Body == null && syntax.ExpressionBody != null, isIterator: SyntaxFacts.HasYieldOperations(syntax.Body), diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs index 7a87de55c1c6c..155a67e773e4c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs @@ -44,7 +44,7 @@ private SourceUserDefinedOperatorSymbol( containingType, location, syntax, - MakeDeclarationModifiers(syntax, location, diagnostics), + MakeDeclarationModifiers(containingType.IsInterface, syntax, location, diagnostics), hasBody: syntax.HasAnyBody(), isExpressionBodied: syntax.Body == null && syntax.ExpressionBody != null, isIterator: SyntaxFacts.HasYieldOperations(syntax.Body), diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs index 8adc46412e49b..d83add495b64c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs @@ -73,9 +73,21 @@ protected SourceUserDefinedOperatorSymbolBase( // SPEC: its operator body consists of a semicolon. For expression-bodied // SPEC: operators, the body is an expression. For all other operators, // SPEC: the operator body consists of a block... - if (hasBody && IsExtern) + if (IsAbstract && IsExtern) { - diagnostics.Add(ErrorCode.ERR_ExternHasBody, location, this); + diagnostics.Add(ErrorCode.ERR_AbstractAndExtern, location, this); + } + else if (hasBody && (IsExtern || IsAbstract)) + { + Debug.Assert(!(IsAbstract && IsExtern)); + if (IsExtern) + { + diagnostics.Add(ErrorCode.ERR_ExternHasBody, location, this); + } + else + { + diagnostics.Add(ErrorCode.ERR_AbstractHasBody, location, this); + } } else if (!hasBody && !IsExtern && !IsAbstract && !IsPartial) { @@ -94,17 +106,66 @@ protected SourceUserDefinedOperatorSymbolBase( } } - protected static DeclarationModifiers MakeDeclarationModifiers(BaseMethodDeclarationSyntax syntax, Location location, BindingDiagnosticBag diagnostics) + protected static DeclarationModifiers MakeDeclarationModifiers(bool inInterface, BaseMethodDeclarationSyntax syntax, Location location, BindingDiagnosticBag diagnostics) { - var defaultAccess = DeclarationModifiers.Private; + var defaultAccess = inInterface ? DeclarationModifiers.Public : DeclarationModifiers.Private; var allowedModifiers = DeclarationModifiers.AccessibilityMask | DeclarationModifiers.Static | DeclarationModifiers.Extern | DeclarationModifiers.Unsafe; - return ModifierUtils.MakeAndCheckNontypeMemberModifiers( + if (inInterface) + { + allowedModifiers |= DeclarationModifiers.Abstract | DeclarationModifiers.Sealed; + } + + var result = ModifierUtils.MakeAndCheckNontypeMemberModifiers( syntax.Modifiers, defaultAccess, allowedModifiers, location, diagnostics, modifierErrors: out _); + + if (inInterface && syntax is OperatorDeclarationSyntax { OperatorToken: var opToken } && + opToken.Kind() is not (SyntaxKind.EqualsEqualsToken or SyntaxKind.ExclamationEqualsToken)) + { + if ((result & (DeclarationModifiers.Abstract | DeclarationModifiers.Sealed)) != 0) + { + if ((result & (DeclarationModifiers.Sealed | DeclarationModifiers.Abstract)) == (DeclarationModifiers.Sealed | DeclarationModifiers.Abstract)) + { + diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, ModifierUtils.ConvertSingleModifierToSyntaxText(DeclarationModifiers.Sealed)); + result &= ~DeclarationModifiers.Sealed; + } + + LanguageVersion availableVersion = ((CSharpParseOptions)location.SourceTree.Options).LanguageVersion; + LanguageVersion requiredVersion = MessageID.IDS_FeatureStaticAbstractMembersInInterfaces.RequiredVersion(); + + if (availableVersion < requiredVersion) + { + var requiredVersionArgument = new CSharpRequiredLanguageVersion(requiredVersion); + var availableVersionArgument = availableVersion.ToDisplayString(); + + reportModifierIfPresent(result, DeclarationModifiers.Abstract, location, diagnostics, requiredVersionArgument, availableVersionArgument); + reportModifierIfPresent(result, DeclarationModifiers.Sealed, location, diagnostics, requiredVersionArgument, availableVersionArgument); + } + + result &= ~DeclarationModifiers.Sealed; + } + else if ((result & DeclarationModifiers.Static) != 0) + { + Binder.CheckFeatureAvailability(location.SourceTree, MessageID.IDS_DefaultInterfaceImplementation, diagnostics, location); + } + } + + return result; + + static void reportModifierIfPresent(DeclarationModifiers result, DeclarationModifiers errorModifier, Location location, BindingDiagnosticBag diagnostics, CSharpRequiredLanguageVersion requiredVersionArgument, string availableVersionArgument) + { + if ((result & errorModifier) != 0) + { + diagnostics.Add(ErrorCode.ERR_InvalidModifierForLanguageVersion, location, + ModifierUtils.ConvertSingleModifierToSyntaxText(errorModifier), + availableVersionArgument, + requiredVersionArgument); + } + } } protected abstract Location ReturnTypeLocation { get; } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 1a3d279bdaef9..ed9f6601c63af 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -972,6 +972,11 @@ Vytvoření objektu s cílovým typem + + static abstract members in interfaces + static abstract members in interfaces + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. Sestavení {0}, které obsahuje typ {1}, se odkazuje na architekturu .NET Framework, což se nepodporuje. @@ -3452,8 +3457,8 @@ - A static member '{0}' cannot be marked as override, virtual, or abstract - Statický člen {0} nemůže být označený klíčovými slovy override, virtual nebo abstract. + A static member cannot be marked as '{0}' + Statický člen {0} nemůže být označený klíčovými slovy override, virtual nebo abstract. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index ef0729679c736..d03315d9d1f47 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -972,6 +972,11 @@ Objekterstellung mit Zieltyp + + static abstract members in interfaces + static abstract members in interfaces + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. Die Assembly "{0}" mit dem Typ "{1}" verweist auf das .NET Framework. Dies wird nicht unterstützt. @@ -3452,8 +3457,8 @@ - A static member '{0}' cannot be marked as override, virtual, or abstract - Ein statischer Member "{0}" kann nicht als "override" , "virtual" oder "abstract" markiert werden. + A static member cannot be marked as '{0}' + Ein statischer Member "{0}" kann nicht als "override" , "virtual" oder "abstract" markiert werden. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 279fcfd98739b..44f634d570134 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -972,6 +972,11 @@ creación de objetos con tipo de destino + + static abstract members in interfaces + static abstract members in interfaces + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. El ensamblado "{0}" que contiene el tipo "{1}" hace referencia a .NET Framework, lo cual no se admite. @@ -3452,8 +3457,8 @@ - A static member '{0}' cannot be marked as override, virtual, or abstract - Un miembro estático '{0}' no se puede marcar como invalidación, virtual o abstracto + A static member cannot be marked as '{0}' + Un miembro estático '{0}' no se puede marcar como invalidación, virtual o abstracto diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 6e9a294f5777c..6d67767d00e8c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -972,6 +972,11 @@ création d'un objet typé cible + + static abstract members in interfaces + static abstract members in interfaces + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. L'assembly '{0}' contenant le type '{1}' référence le .NET Framework, ce qui n'est pas pris en charge. @@ -3452,8 +3457,8 @@ - A static member '{0}' cannot be marked as override, virtual, or abstract - Un membre statique '{0}' ne peut pas être marqué comme override, virtual ou abstract + A static member cannot be marked as '{0}' + Un membre statique '{0}' ne peut pas être marqué comme override, virtual ou abstract diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index ea91aa0a51aea..8788fcdda7fa5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -972,6 +972,11 @@ creazione di oggetti con tipo di destinazione + + static abstract members in interfaces + static abstract members in interfaces + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. L'assembly '{0}' che contiene il tipo '{1}' fa riferimento a .NET Framework, che non è supportato. @@ -3452,8 +3457,8 @@ - A static member '{0}' cannot be marked as override, virtual, or abstract - Un membro statico '{0}' non può essere contrassegnato come override, virtual o abstract + A static member cannot be marked as '{0}' + Un membro statico '{0}' non può essere contrassegnato come override, virtual o abstract diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 8c123cb957639..4e5d10185fbba 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -972,6 +972,11 @@ target-typed オブジェクトの作成 + + static abstract members in interfaces + static abstract members in interfaces + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. 型 '{1}' を含むアセンブリ '{0}' が .NET Framework を参照しています。これはサポートされていません。 @@ -3452,8 +3457,8 @@ - A static member '{0}' cannot be marked as override, virtual, or abstract - 静的メンバー '{0}' を override、virtual、または abstract とすることはできません + A static member cannot be marked as '{0}' + 静的メンバー '{0}' を override、virtual、または abstract とすることはできません diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index cf13519736181..d2c8899d066c0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -972,6 +972,11 @@ 대상으로 형식화된 개체 만들기 + + static abstract members in interfaces + static abstract members in interfaces + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. '{1}' 형식을 포함하는 '{0}' 어셈블리가 지원되지 않는 .NET Framework를 참조합니다. @@ -3451,8 +3456,8 @@ - A static member '{0}' cannot be marked as override, virtual, or abstract - '{0}' 정적 멤버는 override, virtual 또는 abstract로 표시할 수 없습니다. + A static member cannot be marked as '{0}' + '{0}' 정적 멤버는 override, virtual 또는 abstract로 표시할 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 85423d747e0fa..f777be11c85a6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -972,6 +972,11 @@ tworzenie obiektu z typem docelowym + + static abstract members in interfaces + static abstract members in interfaces + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. Zestaw „{0}” zawierający typ „{1}” odwołuje się do platformy .NET Framework, co nie jest obsługiwane. @@ -3452,8 +3457,8 @@ - A static member '{0}' cannot be marked as override, virtual, or abstract - Statycznej składowej „{0}” nie można oznaczyć specyfikatorem override, virtual ani abstract + A static member cannot be marked as '{0}' + Statycznej składowej „{0}” nie można oznaczyć specyfikatorem override, virtual ani abstract diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 961744f06790f..ba4ee95ae89e3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -972,6 +972,11 @@ criação de objeto de tipo de destino + + static abstract members in interfaces + static abstract members in interfaces + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. O assembly '{0}' contendo o tipo '{1}' referencia o .NET Framework, mas não há suporte para isso. @@ -3452,8 +3457,8 @@ - A static member '{0}' cannot be marked as override, virtual, or abstract - Um membro estático "{0}" não pode ser marcado como override, virtual ou abstract + A static member cannot be marked as '{0}' + Um membro estático "{0}" não pode ser marcado como override, virtual ou abstract diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 051e91035a587..fc49fb2900369 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -972,6 +972,11 @@ создание объекта с типом целевого объекта + + static abstract members in interfaces + static abstract members in interfaces + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. Сборка "{0}", содержащая тип "{1}", ссылается на платформу .NET Framework, которая не поддерживается. @@ -3452,8 +3457,8 @@ - A static member '{0}' cannot be marked as override, virtual, or abstract - Статический член "{0}" не может быть помечен как override, virtual или abstract. + A static member cannot be marked as '{0}' + Статический член "{0}" не может быть помечен как override, virtual или abstract. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index af98667809312..f17819d3f997e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -972,6 +972,11 @@ hedeflenen türde nesne oluşturma + + static abstract members in interfaces + static abstract members in interfaces + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. '{1}' türünü içeren '{0}' bütünleştirilmiş kodu, desteklenmeyen .NET Framework'e başvuruyor. @@ -3452,8 +3457,8 @@ - A static member '{0}' cannot be marked as override, virtual, or abstract - '{0}' statik üyesi geçersiz kılınan, sanal veya soyut olarak işaretlenemez + A static member cannot be marked as '{0}' + '{0}' statik üyesi geçersiz kılınan, sanal veya soyut olarak işaretlenemez diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index ace6669e27ffc..a34f064fe0c87 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -972,6 +972,11 @@ 创建目标类型对象 + + static abstract members in interfaces + static abstract members in interfaces + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. 包含类型“{1}”的程序集“{0}”引用了 .NET Framework,而此操作不受支持。 @@ -3457,8 +3462,8 @@ - A static member '{0}' cannot be marked as override, virtual, or abstract - 静态成员“{0}”不能标记为 override、virtual 或 abstract + A static member cannot be marked as '{0}' + 静态成员“{0}”不能标记为 override、virtual 或 abstract diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 8fe0a19ec4a46..5a4d82220ab12 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -972,6 +972,11 @@ 建立具目標類型的物件 + + static abstract members in interfaces + static abstract members in interfaces + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. 包含類型 '{1}' 的組件 '{0}' 參考了 .NET Framework,此情形不受支援。 @@ -3452,8 +3457,8 @@ - A static member '{0}' cannot be marked as override, virtual, or abstract - 靜態成員 '{0}' 不可標記為 override、virtual 或 abstract + A static member cannot be marked as '{0}' + 靜態成員 '{0}' 不可標記為 override、virtual 或 abstract diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs index af01f7b27706b..50ccfecbfdc1a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs @@ -1874,9 +1874,10 @@ internal static virtual void M4() { } // (9,17): error CS0238: 'C.M3()' cannot be sealed because it is not an override // sealed void M3() { } Diagnostic(ErrorCode.ERR_SealedNonOverride, "M3").WithArguments("C.M3()").WithLocation(9, 17), - // (10,34): error CS0112: A static member 'C.M4()' cannot be marked as override, virtual, or abstract + // (10,34): error CS0112: A static member cannot be marked as 'virtual' // internal static virtual void M4() { } - Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M4").WithArguments("C.M4()").WithLocation(10, 34)); + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M4").WithArguments("virtual").WithLocation(10, 34) + ); } [WorkItem(542391, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542391")] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs index c793f70bc77cf..52ce01b7d9c1d 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs @@ -3257,21 +3257,18 @@ class Test1 : I1 Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); compilation1.VerifyDiagnostics( - // (4,22): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + // (4,16): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. // static int P1 => 1; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "1").WithArguments("default interface implementation", "8.0").WithLocation(4, 22), - // (5,21): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "P1").WithArguments("default interface implementation", "8.0").WithLocation(4, 16), + // (5,16): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. // static int P3 { get => 3; } - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "get").WithArguments("default interface implementation", "8.0").WithLocation(5, 21), - // (6,21): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "P3").WithArguments("default interface implementation", "8.0").WithLocation(5, 16), + // (6,16): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. // static int P5 { set => System.Console.WriteLine(5); } - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "set").WithArguments("default interface implementation", "8.0").WithLocation(6, 21), - // (7,21): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "P5").WithArguments("default interface implementation", "8.0").WithLocation(6, 16), + // (7,16): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. // static int P7 { get { return 7;} set {} } - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "get").WithArguments("default interface implementation", "8.0").WithLocation(7, 21), - // (7,38): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // static int P7 { get { return 7;} set {} } - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "set").WithArguments("default interface implementation", "8.0").WithLocation(7, 38) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "P7").WithArguments("default interface implementation", "8.0").WithLocation(7, 16) ); var derived = compilation1.SourceModule.GlobalNamespace.GetTypeMember("Test1"); @@ -5833,12 +5830,9 @@ class Test1 : I1 Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); compilation1.VerifyDiagnostics( - // (6,9): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // add {} - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "add").WithArguments("default interface implementation", "8.0").WithLocation(6, 9), - // (7,9): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // remove {} - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "remove").WithArguments("default interface implementation", "8.0").WithLocation(7, 9) + // (4,32): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + // static event System.Action E7 + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "E7").WithArguments("default interface implementation", "8.0").WithLocation(4, 32) ); var derived = compilation1.GlobalNamespace.GetTypeMember("Test1"); @@ -7012,15 +7006,15 @@ class Test2 : I1 targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (10,24): error CS0238: 'I1.M3()' cannot be sealed because it is not an override + // (10,24): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. // sealed static void M3() - Diagnostic(ErrorCode.ERR_SealedNonOverride, "M3").WithArguments("I1.M3()").WithLocation(10, 24), - // (6,25): error CS0112: A static member 'I1.M2()' cannot be marked as override, virtual, or abstract + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M3").WithArguments("sealed", "9.0", "preview").WithLocation(10, 24), + // (6,25): error CS0112: A static member cannot be marked as 'virtual' // virtual static void M2() - Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M2").WithArguments("I1.M2()").WithLocation(6, 25), - // (4,26): error CS0112: A static member 'I1.M1()' cannot be marked as override, virtual, or abstract + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M2").WithArguments("virtual").WithLocation(6, 25), + // (4,26): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. // abstract static void M1(); - Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M1").WithArguments("I1.M1()").WithLocation(4, 26), + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M1").WithArguments("abstract", "9.0", "preview").WithLocation(4, 26), // (21,13): error CS0539: 'Test1.M4()' in explicit interface declaration is not found among members of the interface that can be implemented // void I1.M4() {} Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M4").WithArguments("Test1.M4()").WithLocation(21, 13), @@ -7053,8 +7047,8 @@ class Test2 : I1 var m2 = i1.GetMember("M2"); Assert.False(m2.IsAbstract); - Assert.True(m2.IsVirtual); - Assert.True(m2.IsMetadataVirtual()); + Assert.False(m2.IsVirtual); + Assert.False(m2.IsMetadataVirtual()); Assert.False(m2.IsSealed); Assert.True(m2.IsStatic); Assert.False(m2.IsExtern); @@ -7068,7 +7062,7 @@ class Test2 : I1 Assert.False(m3.IsAbstract); Assert.False(m3.IsVirtual); Assert.False(m3.IsMetadataVirtual()); - Assert.True(m3.IsSealed); + Assert.False(m3.IsSealed); Assert.True(m3.IsStatic); Assert.False(m3.IsExtern); Assert.False(m3.IsAsync); @@ -8610,9 +8604,9 @@ void I1.M5() {} // (5,27): error CS0621: 'I1.M2()': virtual or abstract members cannot be private // abstract private void M2() {} Diagnostic(ErrorCode.ERR_VirtualPrivate, "M2").WithArguments("I1.M2()").WithLocation(5, 27), - // (6,26): error CS0112: A static member 'I1.M3()' cannot be marked as override, virtual, or abstract + // (6,26): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. // abstract static void M3() {} - Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M3").WithArguments("I1.M3()").WithLocation(6, 26), + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M3").WithArguments("abstract", "9.0", "preview").WithLocation(6, 26), // (11,15): error CS0535: 'Test1' does not implement interface member 'I1.M2()' // class Test1 : I1 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("Test1", "I1.M2()").WithLocation(11, 15), @@ -11293,9 +11287,6 @@ public interface I1 // (9,16): error CS8503: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // static int P06 {get;} Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "P06").WithArguments("static", "7.3", "8.0").WithLocation(9, 16), - // (9,21): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // static int P06 {get;} - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "get").WithArguments("default interface implementation", "8.0").WithLocation(9, 21), // (10,17): error CS8503: The modifier 'virtual' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // virtual int P07 {set;} Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "P07").WithArguments("virtual", "7.3", "8.0").WithLocation(10, 17), @@ -12067,15 +12058,15 @@ class Test2 : I1 targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (4,25): error CS0112: A static member 'I1.P1' cannot be marked as override, virtual, or abstract + // (4,25): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. // abstract static int P1 {get;} - Diagnostic(ErrorCode.ERR_StaticNotVirtual, "P1").WithArguments("I1.P1").WithLocation(4, 25), - // (6,24): error CS0112: A static member 'I1.P2' cannot be marked as override, virtual, or abstract + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "P1").WithArguments("abstract", "9.0", "preview").WithLocation(4, 25), + // (6,24): error CS0112: A static member cannot be marked as 'virtual' // virtual static int P2 {set {}} - Diagnostic(ErrorCode.ERR_StaticNotVirtual, "P2").WithArguments("I1.P2").WithLocation(6, 24), - // (8,23): error CS0238: 'I1.P3' cannot be sealed because it is not an override + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "P2").WithArguments("virtual").WithLocation(6, 24), + // (8,23): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. // sealed static int P3 => 0; - Diagnostic(ErrorCode.ERR_SealedNonOverride, "P3").WithArguments("I1.P3").WithLocation(8, 23), + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "P3").WithArguments("sealed", "9.0", "preview").WithLocation(8, 23), // (15,12): error CS0539: 'Test1.P1' in explicit interface declaration is not found among members of the interface that can be implemented // int I1.P1 => 0; Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "P1").WithArguments("Test1.P1").WithLocation(15, 12), @@ -12119,7 +12110,7 @@ class Test2 : I1 var p2set = p2.SetMethod; Assert.False(p2.IsAbstract); - Assert.True(p2.IsVirtual); + Assert.False(p2.IsVirtual); Assert.False(p2.IsSealed); Assert.True(p2.IsStatic); Assert.False(p2.IsExtern); @@ -12128,8 +12119,8 @@ class Test2 : I1 Assert.Null(test1.FindImplementationForInterfaceMember(p2)); Assert.False(p2set.IsAbstract); - Assert.True(p2set.IsVirtual); - Assert.True(p2set.IsMetadataVirtual()); + Assert.False(p2set.IsVirtual); + Assert.False(p2set.IsMetadataVirtual()); Assert.False(p2set.IsSealed); Assert.True(p2set.IsStatic); Assert.False(p2set.IsExtern); @@ -12143,7 +12134,7 @@ class Test2 : I1 Assert.False(p3.IsAbstract); Assert.False(p3.IsVirtual); - Assert.True(p3.IsSealed); + Assert.False(p3.IsSealed); Assert.True(p3.IsStatic); Assert.False(p3.IsExtern); Assert.False(p3.IsOverride); @@ -12153,7 +12144,7 @@ class Test2 : I1 Assert.False(p3get.IsAbstract); Assert.False(p3get.IsVirtual); Assert.False(p3get.IsMetadataVirtual()); - Assert.True(p3get.IsSealed); + Assert.False(p3get.IsSealed); Assert.True(p3get.IsStatic); Assert.False(p3get.IsExtern); Assert.False(p3get.IsAsync); @@ -14702,19 +14693,19 @@ class Test2 : I1, I2, I3, I4, I5 ValidatePropertyModifiers_18(source1, // (4,22): error CS0500: 'I1.P1.get' cannot declare a body because it is marked abstract // abstract int P1 {get => 0; set => throw null;} - Diagnostic(ErrorCode.ERR_AbstractHasBody, "get").WithArguments("I1.P1.get"), + Diagnostic(ErrorCode.ERR_AbstractHasBody, "get").WithArguments("I1.P1.get").WithLocation(4, 22), // (4,32): error CS0500: 'I1.P1.set' cannot declare a body because it is marked abstract // abstract int P1 {get => 0; set => throw null;} - Diagnostic(ErrorCode.ERR_AbstractHasBody, "set").WithArguments("I1.P1.set"), + Diagnostic(ErrorCode.ERR_AbstractHasBody, "set").WithArguments("I1.P1.set").WithLocation(4, 32), // (8,26): error CS0621: 'I2.P2': virtual or abstract members cannot be private // abstract private int P2 => 0; Diagnostic(ErrorCode.ERR_VirtualPrivate, "P2").WithArguments("I2.P2").WithLocation(8, 26), // (8,32): error CS0500: 'I2.P2.get' cannot declare a body because it is marked abstract // abstract private int P2 => 0; Diagnostic(ErrorCode.ERR_AbstractHasBody, "0").WithArguments("I2.P2.get").WithLocation(8, 32), - // (16,25): error CS0112: A static member 'I4.P4' cannot be marked as override, virtual, or abstract + // (16,25): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. // abstract static int P4 { get {throw null;} set {throw null;}} - Diagnostic(ErrorCode.ERR_StaticNotVirtual, "P4").WithArguments("I4.P4").WithLocation(16, 25), + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "P4").WithArguments("abstract", "9.0", "preview").WithLocation(16, 25), // (16,30): error CS0500: 'I4.P4.get' cannot declare a body because it is marked abstract // abstract static int P4 { get {throw null;} set {throw null;}} Diagnostic(ErrorCode.ERR_AbstractHasBody, "get").WithArguments("I4.P4.get").WithLocation(16, 30), @@ -24102,9 +24093,9 @@ extern event System.Action P11 {add{} remove{}} // (8,38): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. // private event System.Action P05 {remove{}} Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "remove").WithArguments("default interface implementation", "8.0").WithLocation(8, 38), - // (9,37): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + // (9,32): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. // static event System.Action P06 {add{}} - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "add").WithArguments("default interface implementation", "8.0").WithLocation(9, 37), + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "P06").WithArguments("default interface implementation", "8.0").WithLocation(9, 32), // (10,38): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. // virtual event System.Action P07 {remove{}} Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "remove").WithArguments("default interface implementation", "8.0").WithLocation(10, 38), @@ -24901,15 +24892,15 @@ class Test2 : I1 // (8,54): error CS0073: An add or remove accessor must have a body // sealed static event System.Action P3 {add; remove;} Diagnostic(ErrorCode.ERR_AddRemoveMustHaveBody, ";").WithLocation(8, 54), - // (6,40): error CS0112: A static member 'I1.P2' cannot be marked as override, virtual, or abstract + // (6,40): error CS0112: A static member cannot be marked as 'virtual' // virtual static event System.Action P2 {add {} remove{}} - Diagnostic(ErrorCode.ERR_StaticNotVirtual, "P2").WithArguments("I1.P2").WithLocation(6, 40), - // (8,39): error CS0238: 'I1.P3' cannot be sealed because it is not an override + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "P2").WithArguments("virtual").WithLocation(6, 40), + // (8,39): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. // sealed static event System.Action P3 {add; remove;} - Diagnostic(ErrorCode.ERR_SealedNonOverride, "P3").WithArguments("I1.P3").WithLocation(8, 39), - // (4,41): error CS0112: A static member 'I1.P1' cannot be marked as override, virtual, or abstract + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "P3").WithArguments("sealed", "9.0", "preview").WithLocation(8, 39), + // (4,41): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. // abstract static event System.Action P1; - Diagnostic(ErrorCode.ERR_StaticNotVirtual, "P1").WithArguments("I1.P1").WithLocation(4, 41), + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "P1").WithArguments("abstract", "9.0", "preview").WithLocation(4, 41), // (13,28): error CS0539: 'Test1.P1' in explicit interface declaration is not found among members of the interface that can be implemented // event System.Action I1.P1 {add {} remove{}} Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "P1").WithArguments("Test1.P1").WithLocation(13, 28), @@ -24953,7 +24944,7 @@ void ValidateAccessor1(MethodSymbol accessor) var p2 = i1.GetMember("P2"); Assert.False(p2.IsAbstract); - Assert.True(p2.IsVirtual); + Assert.False(p2.IsVirtual); Assert.False(p2.IsSealed); Assert.True(p2.IsStatic); Assert.False(p2.IsExtern); @@ -24966,8 +24957,8 @@ void ValidateAccessor1(MethodSymbol accessor) void ValidateAccessor2(MethodSymbol accessor) { Assert.False(accessor.IsAbstract); - Assert.True(accessor.IsVirtual); - Assert.True(accessor.IsMetadataVirtual()); + Assert.False(accessor.IsVirtual); + Assert.False(accessor.IsMetadataVirtual()); Assert.False(accessor.IsSealed); Assert.True(accessor.IsStatic); Assert.False(accessor.IsExtern); @@ -24981,7 +24972,7 @@ void ValidateAccessor2(MethodSymbol accessor) Assert.False(p3.IsAbstract); Assert.False(p3.IsVirtual); - Assert.True(p3.IsSealed); + Assert.False(p3.IsSealed); Assert.True(p3.IsStatic); Assert.False(p3.IsExtern); Assert.False(p3.IsOverride); @@ -24995,7 +24986,7 @@ void ValidateAccessor3(MethodSymbol accessor) Assert.False(accessor.IsAbstract); Assert.False(accessor.IsVirtual); Assert.False(accessor.IsMetadataVirtual()); - Assert.True(accessor.IsSealed); + Assert.False(accessor.IsSealed); Assert.True(accessor.IsStatic); Assert.False(accessor.IsExtern); Assert.False(accessor.IsAsync); @@ -27055,12 +27046,9 @@ void ValidateP5Accessor(MethodSymbol accessor) // (20,39): error CS8703: The modifier 'extern' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // extern sealed event System.Action P5; Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "P5").WithArguments("extern", "7.3", "8.0").WithLocation(20, 39), - // (26,9): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // add => throw null; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "add").WithArguments("default interface implementation", "8.0").WithLocation(26, 9), - // (27,9): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // remove => throw null; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "remove").WithArguments("default interface implementation", "8.0").WithLocation(27, 9), + // (24,32): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + // static event System.Action P6 + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "P6").WithArguments("default interface implementation", "8.0").WithLocation(24, 32), // (8,40): warning CS0626: Method, operator, or accessor 'I2.P2.remove' is marked external and has no attributes on it. Consider adding a DllImport attribute to specify the external implementation. // virtual extern event System.Action P2; Diagnostic(ErrorCode.WRN_ExternMethodNoImplementation, "P2").WithArguments("I2.P2.remove").WithLocation(8, 40), @@ -27379,9 +27367,9 @@ class Test2 : I1, I2, I3, I4, I5 // (16,44): error CS8712: 'I4.P4': abstract event cannot use event accessor syntax // abstract static event System.Action P4 { add {throw null;} remove {throw null;}} Diagnostic(ErrorCode.ERR_AbstractEventHasAccessors, "{").WithArguments("I4.P4").WithLocation(16, 44), - // (16,41): error CS0112: A static member 'I4.P4' cannot be marked as override, virtual, or abstract + // (16,41): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. // abstract static event System.Action P4 { add {throw null;} remove {throw null;}} - Diagnostic(ErrorCode.ERR_StaticNotVirtual, "P4").WithArguments("I4.P4").WithLocation(16, 41), + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "P4").WithArguments("abstract", "9.0", "preview").WithLocation(16, 41), // (20,41): error CS0106: The modifier 'override' is not valid for this item // override sealed event System.Action P5 { add {throw null;} remove {throw null;}} Diagnostic(ErrorCode.ERR_BadMemberFlag, "P5").WithArguments("override").WithLocation(20, 41), @@ -39260,48 +39248,24 @@ static void Main() // (4,16): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // static int F1 {get; set;} Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "F1").WithArguments("static", "7.3", "8.0").WithLocation(4, 16), - // (4,20): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // static int F1 {get; set;} - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "get").WithArguments("default interface implementation", "8.0").WithLocation(4, 20), - // (4,25): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // static int F1 {get; set;} - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "set").WithArguments("default interface implementation", "8.0").WithLocation(4, 25), // (5,23): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // public static int F2 {get; set;} Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "F2").WithArguments("static", "7.3", "8.0").WithLocation(5, 23), // (5,23): error CS8703: The modifier 'public' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // public static int F2 {get; set;} Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "F2").WithArguments("public", "7.3", "8.0").WithLocation(5, 23), - // (5,27): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // public static int F2 {get; set;} - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "get").WithArguments("default interface implementation", "8.0").WithLocation(5, 27), - // (5,32): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // public static int F2 {get; set;} - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "set").WithArguments("default interface implementation", "8.0").WithLocation(5, 32), // (6,25): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // internal static int F3 {get; set;} Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "F3").WithArguments("static", "7.3", "8.0").WithLocation(6, 25), // (6,25): error CS8703: The modifier 'internal' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // internal static int F3 {get; set;} Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "F3").WithArguments("internal", "7.3", "8.0").WithLocation(6, 25), - // (6,29): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // internal static int F3 {get; set;} - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "get").WithArguments("default interface implementation", "8.0").WithLocation(6, 29), - // (6,34): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // internal static int F3 {get; set;} - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "set").WithArguments("default interface implementation", "8.0").WithLocation(6, 34), // (7,24): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // private static int F4 {get; set;} Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "F4").WithArguments("static", "7.3", "8.0").WithLocation(7, 24), // (7,24): error CS8703: The modifier 'private' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // private static int F4 {get; set;} Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "F4").WithArguments("private", "7.3", "8.0").WithLocation(7, 24), - // (7,28): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // private static int F4 {get; set;} - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "get").WithArguments("default interface implementation", "8.0").WithLocation(7, 28), - // (7,33): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // private static int F4 {get; set;} - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "set").WithArguments("default interface implementation", "8.0").WithLocation(7, 33), // (9,18): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. // public class TestHelper Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "TestHelper").WithArguments("default interface implementation", "8.0").WithLocation(9, 18) @@ -39437,36 +39401,24 @@ static void Main() // (4,16): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // static int F1 {get;} = 1; Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "F1").WithArguments("static", "7.3", "8.0").WithLocation(4, 16), - // (4,20): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // static int F1 {get;} = 1; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "get").WithArguments("default interface implementation", "8.0").WithLocation(4, 20), // (5,23): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // public static int F2 {get;} = 2; Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "F2").WithArguments("static", "7.3", "8.0").WithLocation(5, 23), // (5,23): error CS8703: The modifier 'public' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // public static int F2 {get;} = 2; Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "F2").WithArguments("public", "7.3", "8.0").WithLocation(5, 23), - // (5,27): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // public static int F2 {get;} = 2; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "get").WithArguments("default interface implementation", "8.0").WithLocation(5, 27), // (6,25): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // internal static int F3 {get;} = 3; Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "F3").WithArguments("static", "7.3", "8.0").WithLocation(6, 25), // (6,25): error CS8703: The modifier 'internal' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // internal static int F3 {get;} = 3; Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "F3").WithArguments("internal", "7.3", "8.0").WithLocation(6, 25), - // (6,29): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // internal static int F3 {get;} = 3; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "get").WithArguments("default interface implementation", "8.0").WithLocation(6, 29), // (7,24): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // private static int F4 {get;} = 4; Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "F4").WithArguments("static", "7.3", "8.0").WithLocation(7, 24), // (7,24): error CS8703: The modifier 'private' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // private static int F4 {get;} = 4; Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "F4").WithArguments("private", "7.3", "8.0").WithLocation(7, 24), - // (7,28): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // private static int F4 {get;} = 4; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "get").WithArguments("default interface implementation", "8.0").WithLocation(7, 28), // (9,18): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. // public class TestHelper Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "TestHelper").WithArguments("default interface implementation", "8.0").WithLocation(9, 18) @@ -39588,45 +39540,27 @@ static void Main() // (4,16): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // static int F1 {get; private set;} Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "F1").WithArguments("static", "7.3", "8.0").WithLocation(4, 16), - // (4,20): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // static int F1 {get; private set;} - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "get").WithArguments("default interface implementation", "8.0").WithLocation(4, 20), // (4,33): error CS8703: The modifier 'private' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // static int F1 {get; private set;} Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "set").WithArguments("private", "7.3", "8.0").WithLocation(4, 33), - // (4,33): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // static int F1 {get; private set;} - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "set").WithArguments("default interface implementation", "8.0").WithLocation(4, 33), // (5,23): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // public static int F2 {get; private set;} Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "F2").WithArguments("static", "7.3", "8.0").WithLocation(5, 23), // (5,23): error CS8703: The modifier 'public' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // public static int F2 {get; private set;} Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "F2").WithArguments("public", "7.3", "8.0").WithLocation(5, 23), - // (5,27): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // public static int F2 {get; private set;} - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "get").WithArguments("default interface implementation", "8.0").WithLocation(5, 27), // (5,40): error CS8703: The modifier 'private' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // public static int F2 {get; private set;} Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "set").WithArguments("private", "7.3", "8.0").WithLocation(5, 40), - // (5,40): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // public static int F2 {get; private set;} - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "set").WithArguments("default interface implementation", "8.0").WithLocation(5, 40), // (6,25): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // internal static int F3 {get; private set;} Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "F3").WithArguments("static", "7.3", "8.0").WithLocation(6, 25), // (6,25): error CS8703: The modifier 'internal' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // internal static int F3 {get; private set;} Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "F3").WithArguments("internal", "7.3", "8.0").WithLocation(6, 25), - // (6,29): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // internal static int F3 {get; private set;} - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "get").WithArguments("default interface implementation", "8.0").WithLocation(6, 29), // (6,42): error CS8703: The modifier 'private' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. // internal static int F3 {get; private set;} Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "set").WithArguments("private", "7.3", "8.0").WithLocation(6, 42), - // (6,42): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. - // internal static int F3 {get; private set;} - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "set").WithArguments("default interface implementation", "8.0").WithLocation(6, 42), // (8,18): error CS8652: The feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. // public class TestHelper Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "TestHelper").WithArguments("default interface implementation", "8.0").WithLocation(8, 18) diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs new file mode 100644 index 0000000000000..fb60e77189c17 --- /dev/null +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -0,0 +1,4145 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable disable + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols +{ + public class StaticAbstractMembersInInterfacesTests : CSharpTestBase + { + [Fact] + public void MethodModifiers_01() + { + var source1 = +@" +public interface I1 +{ + abstract static void M01() + ; + + virtual static void M02() + ; + + sealed static void M03() + ; + + override static void M04() + ; + + abstract virtual static void M05() + ; + + abstract sealed static void M06() + ; + + abstract override static void M07() + ; + + virtual sealed static void M08() + ; + + virtual override static void M09() + ; + + sealed override static void M10() + ; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,26): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static void M01() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "9.0", "preview").WithLocation(4, 26), + // (7,25): error CS0112: A static member cannot be marked as 'virtual' + // virtual static void M02() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M02").WithArguments("virtual").WithLocation(7, 25), + // (7,25): error CS0501: 'I1.M02()' must declare a body because it is not marked abstract, extern, or partial + // virtual static void M02() + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "M02").WithArguments("I1.M02()").WithLocation(7, 25), + // (10,24): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // sealed static void M03() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M03").WithArguments("sealed", "9.0", "preview").WithLocation(10, 24), + // (10,24): error CS0501: 'I1.M03()' must declare a body because it is not marked abstract, extern, or partial + // sealed static void M03() + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "M03").WithArguments("I1.M03()").WithLocation(10, 24), + // (13,26): error CS0106: The modifier 'override' is not valid for this item + // override static void M04() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M04").WithArguments("override").WithLocation(13, 26), + // (13,26): error CS0501: 'I1.M04()' must declare a body because it is not marked abstract, extern, or partial + // override static void M04() + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "M04").WithArguments("I1.M04()").WithLocation(13, 26), + // (16,34): error CS0112: A static member cannot be marked as 'virtual' + // abstract virtual static void M05() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M05").WithArguments("virtual").WithLocation(16, 34), + // (16,34): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract virtual static void M05() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M05").WithArguments("abstract", "9.0", "preview").WithLocation(16, 34), + // (19,33): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static void M06() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M06").WithArguments("sealed").WithLocation(19, 33), + // (19,33): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract sealed static void M06() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M06").WithArguments("abstract", "9.0", "preview").WithLocation(19, 33), + // (22,35): error CS0106: The modifier 'override' is not valid for this item + // abstract override static void M07() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M07").WithArguments("override").WithLocation(22, 35), + // (22,35): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract override static void M07() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M07").WithArguments("abstract", "9.0", "preview").WithLocation(22, 35), + // (25,32): error CS0112: A static member cannot be marked as 'virtual' + // virtual sealed static void M08() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M08").WithArguments("virtual").WithLocation(25, 32), + // (25,32): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // virtual sealed static void M08() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M08").WithArguments("sealed", "9.0", "preview").WithLocation(25, 32), + // (25,32): error CS0501: 'I1.M08()' must declare a body because it is not marked abstract, extern, or partial + // virtual sealed static void M08() + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "M08").WithArguments("I1.M08()").WithLocation(25, 32), + // (28,34): error CS0112: A static member cannot be marked as 'virtual' + // virtual override static void M09() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M09").WithArguments("virtual").WithLocation(28, 34), + // (28,34): error CS0106: The modifier 'override' is not valid for this item + // virtual override static void M09() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M09").WithArguments("override").WithLocation(28, 34), + // (28,34): error CS0501: 'I1.M09()' must declare a body because it is not marked abstract, extern, or partial + // virtual override static void M09() + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "M09").WithArguments("I1.M09()").WithLocation(28, 34), + // (31,33): error CS0106: The modifier 'override' is not valid for this item + // sealed override static void M10() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M10").WithArguments("override").WithLocation(31, 33), + // (31,33): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // sealed override static void M10() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M10").WithArguments("sealed", "9.0", "preview").WithLocation(31, 33), + // (31,33): error CS0501: 'I1.M10()' must declare a body because it is not marked abstract, extern, or partial + // sealed override static void M10() + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "M10").WithArguments("I1.M10()").WithLocation(31, 33) + ); + + ValidateMethodModifiers_01(compilation1); + } + + private static void ValidateMethodModifiers_01(CSharpCompilation compilation1) + { + var i1 = compilation1.GetTypeByMetadataName("I1"); + var m01 = i1.GetMember("M01"); + + Assert.True(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.True(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + var m02 = i1.GetMember("M02"); + + Assert.False(m02.IsAbstract); + Assert.False(m02.IsVirtual); + Assert.False(m02.IsMetadataVirtual()); + Assert.False(m02.IsSealed); + Assert.True(m02.IsStatic); + Assert.False(m02.IsExtern); + Assert.False(m02.IsAsync); + Assert.False(m02.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m02)); + + var m03 = i1.GetMember("M03"); + + Assert.False(m03.IsAbstract); + Assert.False(m03.IsVirtual); + Assert.False(m03.IsMetadataVirtual()); + Assert.False(m03.IsSealed); + Assert.True(m03.IsStatic); + Assert.False(m03.IsExtern); + Assert.False(m03.IsAsync); + Assert.False(m03.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m03)); + + var m04 = i1.GetMember("M04"); + + Assert.False(m04.IsAbstract); + Assert.False(m04.IsVirtual); + Assert.False(m04.IsMetadataVirtual()); + Assert.False(m04.IsSealed); + Assert.True(m04.IsStatic); + Assert.False(m04.IsExtern); + Assert.False(m04.IsAsync); + Assert.False(m04.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m04)); + + var m05 = i1.GetMember("M05"); + + Assert.True(m05.IsAbstract); + Assert.False(m05.IsVirtual); + Assert.True(m05.IsMetadataVirtual()); + Assert.False(m05.IsSealed); + Assert.True(m05.IsStatic); + Assert.False(m05.IsExtern); + Assert.False(m05.IsAsync); + Assert.False(m05.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m05)); + + var m06 = i1.GetMember("M06"); + + Assert.True(m06.IsAbstract); + Assert.False(m06.IsVirtual); + Assert.True(m06.IsMetadataVirtual()); + Assert.False(m06.IsSealed); + Assert.True(m06.IsStatic); + Assert.False(m06.IsExtern); + Assert.False(m06.IsAsync); + Assert.False(m06.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m06)); + + var m07 = i1.GetMember("M07"); + + Assert.True(m07.IsAbstract); + Assert.False(m07.IsVirtual); + Assert.True(m07.IsMetadataVirtual()); + Assert.False(m07.IsSealed); + Assert.True(m07.IsStatic); + Assert.False(m07.IsExtern); + Assert.False(m07.IsAsync); + Assert.False(m07.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m07)); + + var m08 = i1.GetMember("M08"); + + Assert.False(m08.IsAbstract); + Assert.False(m08.IsVirtual); + Assert.False(m08.IsMetadataVirtual()); + Assert.False(m08.IsSealed); + Assert.True(m08.IsStatic); + Assert.False(m08.IsExtern); + Assert.False(m08.IsAsync); + Assert.False(m08.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m08)); + + var m09 = i1.GetMember("M09"); + + Assert.False(m09.IsAbstract); + Assert.False(m09.IsVirtual); + Assert.False(m09.IsMetadataVirtual()); + Assert.False(m09.IsSealed); + Assert.True(m09.IsStatic); + Assert.False(m09.IsExtern); + Assert.False(m09.IsAsync); + Assert.False(m09.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m09)); + + var m10 = i1.GetMember("M10"); + + Assert.False(m10.IsAbstract); + Assert.False(m10.IsVirtual); + Assert.False(m10.IsMetadataVirtual()); + Assert.False(m10.IsSealed); + Assert.True(m10.IsStatic); + Assert.False(m10.IsExtern); + Assert.False(m10.IsAsync); + Assert.False(m10.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m10)); + } + + [Fact] + public void MethodModifiers_02() + { + var source1 = +@" +public interface I1 +{ + abstract static void M01() + {} + + virtual static void M02() + {} + + sealed static void M03() + {} + + override static void M04() + {} + + abstract virtual static void M05() + {} + + abstract sealed static void M06() + {} + + abstract override static void M07() + {} + + virtual sealed static void M08() + {} + + virtual override static void M09() + {} + + sealed override static void M10() + {} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,26): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static void M01() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "9.0", "preview").WithLocation(4, 26), + // (4,26): error CS0500: 'I1.M01()' cannot declare a body because it is marked abstract + // abstract static void M01() + Diagnostic(ErrorCode.ERR_AbstractHasBody, "M01").WithArguments("I1.M01()").WithLocation(4, 26), + // (7,25): error CS0112: A static member cannot be marked as 'virtual' + // virtual static void M02() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M02").WithArguments("virtual").WithLocation(7, 25), + // (10,24): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // sealed static void M03() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M03").WithArguments("sealed", "9.0", "preview").WithLocation(10, 24), + // (13,26): error CS0106: The modifier 'override' is not valid for this item + // override static void M04() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M04").WithArguments("override").WithLocation(13, 26), + // (16,34): error CS0112: A static member cannot be marked as 'virtual' + // abstract virtual static void M05() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M05").WithArguments("virtual").WithLocation(16, 34), + // (16,34): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract virtual static void M05() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M05").WithArguments("abstract", "9.0", "preview").WithLocation(16, 34), + // (16,34): error CS0500: 'I1.M05()' cannot declare a body because it is marked abstract + // abstract virtual static void M05() + Diagnostic(ErrorCode.ERR_AbstractHasBody, "M05").WithArguments("I1.M05()").WithLocation(16, 34), + // (19,33): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static void M06() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M06").WithArguments("sealed").WithLocation(19, 33), + // (19,33): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract sealed static void M06() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M06").WithArguments("abstract", "9.0", "preview").WithLocation(19, 33), + // (19,33): error CS0500: 'I1.M06()' cannot declare a body because it is marked abstract + // abstract sealed static void M06() + Diagnostic(ErrorCode.ERR_AbstractHasBody, "M06").WithArguments("I1.M06()").WithLocation(19, 33), + // (22,35): error CS0106: The modifier 'override' is not valid for this item + // abstract override static void M07() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M07").WithArguments("override").WithLocation(22, 35), + // (22,35): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract override static void M07() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M07").WithArguments("abstract", "9.0", "preview").WithLocation(22, 35), + // (22,35): error CS0500: 'I1.M07()' cannot declare a body because it is marked abstract + // abstract override static void M07() + Diagnostic(ErrorCode.ERR_AbstractHasBody, "M07").WithArguments("I1.M07()").WithLocation(22, 35), + // (25,32): error CS0112: A static member cannot be marked as 'virtual' + // virtual sealed static void M08() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M08").WithArguments("virtual").WithLocation(25, 32), + // (25,32): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // virtual sealed static void M08() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M08").WithArguments("sealed", "9.0", "preview").WithLocation(25, 32), + // (28,34): error CS0112: A static member cannot be marked as 'virtual' + // virtual override static void M09() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M09").WithArguments("virtual").WithLocation(28, 34), + // (28,34): error CS0106: The modifier 'override' is not valid for this item + // virtual override static void M09() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M09").WithArguments("override").WithLocation(28, 34), + // (31,33): error CS0106: The modifier 'override' is not valid for this item + // sealed override static void M10() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M10").WithArguments("override").WithLocation(31, 33), + // (31,33): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // sealed override static void M10() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M10").WithArguments("sealed", "9.0", "preview").WithLocation(31, 33) + ); + + ValidateMethodModifiers_01(compilation1); + } + + [Fact] + public void MethodModifiers_03() + { + var source1 = +@" +public interface I1 +{ + abstract static void M01() + ; + + virtual static void M02() + ; + + sealed static void M03() + ; + + override static void M04() + ; + + abstract virtual static void M05() + ; + + abstract sealed static void M06() + ; + + abstract override static void M07() + ; + + virtual sealed static void M08() + ; + + virtual override static void M09() + ; + + sealed override static void M10() + ; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (7,25): error CS0112: A static member cannot be marked as 'virtual' + // virtual static void M02() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M02").WithArguments("virtual").WithLocation(7, 25), + // (7,25): error CS0501: 'I1.M02()' must declare a body because it is not marked abstract, extern, or partial + // virtual static void M02() + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "M02").WithArguments("I1.M02()").WithLocation(7, 25), + // (10,24): error CS0501: 'I1.M03()' must declare a body because it is not marked abstract, extern, or partial + // sealed static void M03() + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "M03").WithArguments("I1.M03()").WithLocation(10, 24), + // (13,26): error CS0106: The modifier 'override' is not valid for this item + // override static void M04() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M04").WithArguments("override").WithLocation(13, 26), + // (13,26): error CS0501: 'I1.M04()' must declare a body because it is not marked abstract, extern, or partial + // override static void M04() + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "M04").WithArguments("I1.M04()").WithLocation(13, 26), + // (16,34): error CS0112: A static member cannot be marked as 'virtual' + // abstract virtual static void M05() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M05").WithArguments("virtual").WithLocation(16, 34), + // (19,33): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static void M06() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M06").WithArguments("sealed").WithLocation(19, 33), + // (22,35): error CS0106: The modifier 'override' is not valid for this item + // abstract override static void M07() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M07").WithArguments("override").WithLocation(22, 35), + // (25,32): error CS0112: A static member cannot be marked as 'virtual' + // virtual sealed static void M08() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M08").WithArguments("virtual").WithLocation(25, 32), + // (25,32): error CS0501: 'I1.M08()' must declare a body because it is not marked abstract, extern, or partial + // virtual sealed static void M08() + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "M08").WithArguments("I1.M08()").WithLocation(25, 32), + // (28,34): error CS0112: A static member cannot be marked as 'virtual' + // virtual override static void M09() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M09").WithArguments("virtual").WithLocation(28, 34), + // (28,34): error CS0106: The modifier 'override' is not valid for this item + // virtual override static void M09() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M09").WithArguments("override").WithLocation(28, 34), + // (28,34): error CS0501: 'I1.M09()' must declare a body because it is not marked abstract, extern, or partial + // virtual override static void M09() + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "M09").WithArguments("I1.M09()").WithLocation(28, 34), + // (31,33): error CS0106: The modifier 'override' is not valid for this item + // sealed override static void M10() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M10").WithArguments("override").WithLocation(31, 33), + // (31,33): error CS0501: 'I1.M10()' must declare a body because it is not marked abstract, extern, or partial + // sealed override static void M10() + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "M10").WithArguments("I1.M10()").WithLocation(31, 33) + ); + + ValidateMethodModifiers_01(compilation1); + } + + [Fact] + public void MethodModifiers_04() + { + var source1 = +@" +public interface I1 +{ + abstract static void M01() + {} + + virtual static void M02() + {} + + sealed static void M03() + {} + + override static void M04() + {} + + abstract virtual static void M05() + {} + + abstract sealed static void M06() + {} + + abstract override static void M07() + {} + + virtual sealed static void M08() + {} + + virtual override static void M09() + {} + + sealed override static void M10() + {} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,26): error CS0500: 'I1.M01()' cannot declare a body because it is marked abstract + // abstract static void M01() + Diagnostic(ErrorCode.ERR_AbstractHasBody, "M01").WithArguments("I1.M01()").WithLocation(4, 26), + // (7,25): error CS0112: A static member cannot be marked as 'virtual' + // virtual static void M02() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M02").WithArguments("virtual").WithLocation(7, 25), + // (13,26): error CS0106: The modifier 'override' is not valid for this item + // override static void M04() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M04").WithArguments("override").WithLocation(13, 26), + // (16,34): error CS0112: A static member cannot be marked as 'virtual' + // abstract virtual static void M05() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M05").WithArguments("virtual").WithLocation(16, 34), + // (16,34): error CS0500: 'I1.M05()' cannot declare a body because it is marked abstract + // abstract virtual static void M05() + Diagnostic(ErrorCode.ERR_AbstractHasBody, "M05").WithArguments("I1.M05()").WithLocation(16, 34), + // (19,33): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static void M06() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M06").WithArguments("sealed").WithLocation(19, 33), + // (19,33): error CS0500: 'I1.M06()' cannot declare a body because it is marked abstract + // abstract sealed static void M06() + Diagnostic(ErrorCode.ERR_AbstractHasBody, "M06").WithArguments("I1.M06()").WithLocation(19, 33), + // (22,35): error CS0106: The modifier 'override' is not valid for this item + // abstract override static void M07() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M07").WithArguments("override").WithLocation(22, 35), + // (22,35): error CS0500: 'I1.M07()' cannot declare a body because it is marked abstract + // abstract override static void M07() + Diagnostic(ErrorCode.ERR_AbstractHasBody, "M07").WithArguments("I1.M07()").WithLocation(22, 35), + // (25,32): error CS0112: A static member cannot be marked as 'virtual' + // virtual sealed static void M08() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M08").WithArguments("virtual").WithLocation(25, 32), + // (28,34): error CS0112: A static member cannot be marked as 'virtual' + // virtual override static void M09() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M09").WithArguments("virtual").WithLocation(28, 34), + // (28,34): error CS0106: The modifier 'override' is not valid for this item + // virtual override static void M09() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M09").WithArguments("override").WithLocation(28, 34), + // (31,33): error CS0106: The modifier 'override' is not valid for this item + // sealed override static void M10() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M10").WithArguments("override").WithLocation(31, 33) + ); + + ValidateMethodModifiers_01(compilation1); + } + + [Fact] + public void MethodModifiers_05() + { + var source1 = +@" +public interface I1 +{ + abstract static void M01() + ; + + virtual static void M02() + ; + + sealed static void M03() + ; + + override static void M04() + ; + + abstract virtual static void M05() + ; + + abstract sealed static void M06() + ; + + abstract override static void M07() + ; + + virtual sealed static void M08() + ; + + virtual override static void M09() + ; + + sealed override static void M10() + ; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular7_3, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,26): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract static void M01() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "7.3", "preview").WithLocation(4, 26), + // (7,25): error CS0112: A static member cannot be marked as 'virtual' + // virtual static void M02() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M02").WithArguments("virtual").WithLocation(7, 25), + // (7,25): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. + // virtual static void M02() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M02").WithArguments("static", "7.3", "8.0").WithLocation(7, 25), + // (7,25): error CS0501: 'I1.M02()' must declare a body because it is not marked abstract, extern, or partial + // virtual static void M02() + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "M02").WithArguments("I1.M02()").WithLocation(7, 25), + // (10,24): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // sealed static void M03() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M03").WithArguments("sealed", "7.3", "preview").WithLocation(10, 24), + // (10,24): error CS0501: 'I1.M03()' must declare a body because it is not marked abstract, extern, or partial + // sealed static void M03() + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "M03").WithArguments("I1.M03()").WithLocation(10, 24), + // (13,26): error CS0106: The modifier 'override' is not valid for this item + // override static void M04() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M04").WithArguments("override").WithLocation(13, 26), + // (13,26): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. + // override static void M04() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M04").WithArguments("static", "7.3", "8.0").WithLocation(13, 26), + // (13,26): error CS0501: 'I1.M04()' must declare a body because it is not marked abstract, extern, or partial + // override static void M04() + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "M04").WithArguments("I1.M04()").WithLocation(13, 26), + // (16,34): error CS0112: A static member cannot be marked as 'virtual' + // abstract virtual static void M05() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M05").WithArguments("virtual").WithLocation(16, 34), + // (16,34): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract virtual static void M05() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M05").WithArguments("abstract", "7.3", "preview").WithLocation(16, 34), + // (19,33): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static void M06() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M06").WithArguments("sealed").WithLocation(19, 33), + // (19,33): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract sealed static void M06() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M06").WithArguments("abstract", "7.3", "preview").WithLocation(19, 33), + // (22,35): error CS0106: The modifier 'override' is not valid for this item + // abstract override static void M07() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M07").WithArguments("override").WithLocation(22, 35), + // (22,35): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract override static void M07() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M07").WithArguments("abstract", "7.3", "preview").WithLocation(22, 35), + // (25,32): error CS0112: A static member cannot be marked as 'virtual' + // virtual sealed static void M08() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M08").WithArguments("virtual").WithLocation(25, 32), + // (25,32): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // virtual sealed static void M08() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M08").WithArguments("sealed", "7.3", "preview").WithLocation(25, 32), + // (25,32): error CS0501: 'I1.M08()' must declare a body because it is not marked abstract, extern, or partial + // virtual sealed static void M08() + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "M08").WithArguments("I1.M08()").WithLocation(25, 32), + // (28,34): error CS0112: A static member cannot be marked as 'virtual' + // virtual override static void M09() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M09").WithArguments("virtual").WithLocation(28, 34), + // (28,34): error CS0106: The modifier 'override' is not valid for this item + // virtual override static void M09() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M09").WithArguments("override").WithLocation(28, 34), + // (28,34): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. + // virtual override static void M09() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M09").WithArguments("static", "7.3", "8.0").WithLocation(28, 34), + // (28,34): error CS0501: 'I1.M09()' must declare a body because it is not marked abstract, extern, or partial + // virtual override static void M09() + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "M09").WithArguments("I1.M09()").WithLocation(28, 34), + // (31,33): error CS0106: The modifier 'override' is not valid for this item + // sealed override static void M10() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M10").WithArguments("override").WithLocation(31, 33), + // (31,33): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // sealed override static void M10() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M10").WithArguments("sealed", "7.3", "preview").WithLocation(31, 33), + // (31,33): error CS0501: 'I1.M10()' must declare a body because it is not marked abstract, extern, or partial + // sealed override static void M10() + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "M10").WithArguments("I1.M10()").WithLocation(31, 33) + ); + + ValidateMethodModifiers_01(compilation1); + } + + [Fact] + public void MethodModifiers_06() + { + var source1 = +@" +public interface I1 +{ + abstract static void M01() + {} + + virtual static void M02() + {} + + sealed static void M03() + {} + + override static void M04() + {} + + abstract virtual static void M05() + {} + + abstract sealed static void M06() + {} + + abstract override static void M07() + {} + + virtual sealed static void M08() + {} + + virtual override static void M09() + {} + + sealed override static void M10() + {} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular7_3, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,26): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract static void M01() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "7.3", "preview").WithLocation(4, 26), + // (4,26): error CS0500: 'I1.M01()' cannot declare a body because it is marked abstract + // abstract static void M01() + Diagnostic(ErrorCode.ERR_AbstractHasBody, "M01").WithArguments("I1.M01()").WithLocation(4, 26), + // (7,25): error CS0112: A static member cannot be marked as 'virtual' + // virtual static void M02() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M02").WithArguments("virtual").WithLocation(7, 25), + // (7,25): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + // virtual static void M02() + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "M02").WithArguments("default interface implementation", "8.0").WithLocation(7, 25), + // (10,24): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // sealed static void M03() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M03").WithArguments("sealed", "7.3", "preview").WithLocation(10, 24), + // (13,26): error CS0106: The modifier 'override' is not valid for this item + // override static void M04() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M04").WithArguments("override").WithLocation(13, 26), + // (13,26): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + // override static void M04() + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "M04").WithArguments("default interface implementation", "8.0").WithLocation(13, 26), + // (16,34): error CS0112: A static member cannot be marked as 'virtual' + // abstract virtual static void M05() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M05").WithArguments("virtual").WithLocation(16, 34), + // (16,34): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract virtual static void M05() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M05").WithArguments("abstract", "7.3", "preview").WithLocation(16, 34), + // (16,34): error CS0500: 'I1.M05()' cannot declare a body because it is marked abstract + // abstract virtual static void M05() + Diagnostic(ErrorCode.ERR_AbstractHasBody, "M05").WithArguments("I1.M05()").WithLocation(16, 34), + // (19,33): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static void M06() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M06").WithArguments("sealed").WithLocation(19, 33), + // (19,33): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract sealed static void M06() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M06").WithArguments("abstract", "7.3", "preview").WithLocation(19, 33), + // (19,33): error CS0500: 'I1.M06()' cannot declare a body because it is marked abstract + // abstract sealed static void M06() + Diagnostic(ErrorCode.ERR_AbstractHasBody, "M06").WithArguments("I1.M06()").WithLocation(19, 33), + // (22,35): error CS0106: The modifier 'override' is not valid for this item + // abstract override static void M07() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M07").WithArguments("override").WithLocation(22, 35), + // (22,35): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract override static void M07() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M07").WithArguments("abstract", "7.3", "preview").WithLocation(22, 35), + // (22,35): error CS0500: 'I1.M07()' cannot declare a body because it is marked abstract + // abstract override static void M07() + Diagnostic(ErrorCode.ERR_AbstractHasBody, "M07").WithArguments("I1.M07()").WithLocation(22, 35), + // (25,32): error CS0112: A static member cannot be marked as 'virtual' + // virtual sealed static void M08() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M08").WithArguments("virtual").WithLocation(25, 32), + // (25,32): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // virtual sealed static void M08() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M08").WithArguments("sealed", "7.3", "preview").WithLocation(25, 32), + // (28,34): error CS0112: A static member cannot be marked as 'virtual' + // virtual override static void M09() + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M09").WithArguments("virtual").WithLocation(28, 34), + // (28,34): error CS0106: The modifier 'override' is not valid for this item + // virtual override static void M09() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M09").WithArguments("override").WithLocation(28, 34), + // (28,34): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + // virtual override static void M09() + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "M09").WithArguments("default interface implementation", "8.0").WithLocation(28, 34), + // (31,33): error CS0106: The modifier 'override' is not valid for this item + // sealed override static void M10() + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M10").WithArguments("override").WithLocation(31, 33), + // (31,33): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // sealed override static void M10() + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M10").WithArguments("sealed", "7.3", "preview").WithLocation(31, 33) + ); + + ValidateMethodModifiers_01(compilation1); + } + + [Fact] + public void SealedStaticConstructor_01() + { + var source1 = +@" +interface I1 +{ + sealed static I1() {} +} + +partial interface I2 +{ + partial sealed static I2(); +} + +partial interface I2 +{ + partial static I2() {} +} + +partial interface I3 +{ + partial static I3(); +} + +partial interface I3 +{ + partial sealed static I3() {} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,19): error CS0106: The modifier 'sealed' is not valid for this item + // sealed static I1() {} + Diagnostic(ErrorCode.ERR_BadMemberFlag, "I1").WithArguments("sealed").WithLocation(4, 19), + // (9,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // partial sealed static I2(); + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(9, 5), + // (9,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // partial sealed static I2(); + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(9, 5), + // (9,27): error CS0106: The modifier 'sealed' is not valid for this item + // partial sealed static I2(); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "I2").WithArguments("sealed").WithLocation(9, 27), + // (14,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // partial static I2() {} + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(14, 5), + // (14,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // partial static I2() {} + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(14, 5), + // (14,20): error CS0111: Type 'I2' already defines a member called 'I2' with the same parameter types + // partial static I2() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "I2").WithArguments("I2", "I2").WithLocation(14, 20), + // (19,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // partial static I3(); + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(19, 5), + // (19,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // partial static I3(); + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(19, 5), + // (24,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // partial sealed static I3() {} + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(24, 5), + // (24,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // partial sealed static I3() {} + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(24, 5), + // (24,27): error CS0106: The modifier 'sealed' is not valid for this item + // partial sealed static I3() {} + Diagnostic(ErrorCode.ERR_BadMemberFlag, "I3").WithArguments("sealed").WithLocation(24, 27), + // (24,27): error CS0111: Type 'I3' already defines a member called 'I3' with the same parameter types + // partial sealed static I3() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "I3").WithArguments("I3", "I3").WithLocation(24, 27) + ); + + var i1 = compilation1.GetTypeByMetadataName("I1"); + var m01 = i1.GetMember(".cctor"); + + Assert.False(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.False(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + } + + [Fact] + public void SealedStaticConstructor_02() + { + var source1 = +@" +partial interface I2 +{ + sealed static partial I2(); +} + +partial interface I2 +{ + static partial I2() {} +} + +partial interface I3 +{ + static partial I3(); +} + +partial interface I3 +{ + sealed static partial I3() {} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,19): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // sealed static partial I2(); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(4, 19), + // (4,27): error CS0501: 'I2.I2()' must declare a body because it is not marked abstract, extern, or partial + // sealed static partial I2(); + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "I2").WithArguments("I2.I2()").WithLocation(4, 27), + // (4,27): error CS0542: 'I2': member names cannot be the same as their enclosing type + // sealed static partial I2(); + Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "I2").WithArguments("I2").WithLocation(4, 27), + // (9,12): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // static partial I2() {} + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(9, 12), + // (9,20): error CS0542: 'I2': member names cannot be the same as their enclosing type + // static partial I2() {} + Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "I2").WithArguments("I2").WithLocation(9, 20), + // (9,20): error CS0111: Type 'I2' already defines a member called 'I2' with the same parameter types + // static partial I2() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "I2").WithArguments("I2", "I2").WithLocation(9, 20), + // (9,20): error CS0161: 'I2.I2()': not all code paths return a value + // static partial I2() {} + Diagnostic(ErrorCode.ERR_ReturnExpected, "I2").WithArguments("I2.I2()").WithLocation(9, 20), + // (14,12): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // static partial I3(); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(14, 12), + // (14,20): error CS0501: 'I3.I3()' must declare a body because it is not marked abstract, extern, or partial + // static partial I3(); + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "I3").WithArguments("I3.I3()").WithLocation(14, 20), + // (14,20): error CS0542: 'I3': member names cannot be the same as their enclosing type + // static partial I3(); + Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "I3").WithArguments("I3").WithLocation(14, 20), + // (19,19): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // sealed static partial I3() {} + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(19, 19), + // (19,27): error CS0542: 'I3': member names cannot be the same as their enclosing type + // sealed static partial I3() {} + Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "I3").WithArguments("I3").WithLocation(19, 27), + // (19,27): error CS0111: Type 'I3' already defines a member called 'I3' with the same parameter types + // sealed static partial I3() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "I3").WithArguments("I3", "I3").WithLocation(19, 27), + // (19,27): error CS0161: 'I3.I3()': not all code paths return a value + // sealed static partial I3() {} + Diagnostic(ErrorCode.ERR_ReturnExpected, "I3").WithArguments("I3.I3()").WithLocation(19, 27) + ); + } + + [Fact] + public void AbstractStaticConstructor_01() + { + var source1 = +@" +interface I1 +{ + abstract static I1(); +} + +interface I2 +{ + abstract static I2() {} +} + +interface I3 +{ + static I3(); +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,21): error CS0106: The modifier 'abstract' is not valid for this item + // abstract static I1(); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "I1").WithArguments("abstract").WithLocation(4, 21), + // (9,21): error CS0106: The modifier 'abstract' is not valid for this item + // abstract static I2() {} + Diagnostic(ErrorCode.ERR_BadMemberFlag, "I2").WithArguments("abstract").WithLocation(9, 21), + // (14,12): error CS0501: 'I3.I3()' must declare a body because it is not marked abstract, extern, or partial + // static I3(); + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "I3").WithArguments("I3.I3()").WithLocation(14, 12) + ); + + var i1 = compilation1.GetTypeByMetadataName("I1"); + var m01 = i1.GetMember(".cctor"); + + Assert.False(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.False(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + } + + [Fact] + public void PartialSealedStatic_01() + { + var source1 = +@" +partial interface I1 +{ + sealed static partial void M01(); +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var i1 = compilation1.GetTypeByMetadataName("I1"); + var m01 = i1.GetMember("M01"); + + Assert.False(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.False(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + Assert.True(m01.IsPartialDefinition()); + Assert.Null(m01.PartialImplementationPart); + } + + [Fact] + public void PartialSealedStatic_02() + { + var source1 = +@" +partial interface I1 +{ + sealed static partial void M01(); +} +partial interface I1 +{ + sealed static partial void M01() {} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + ValidatePartialSealedStatic_02(compilation1); + } + + private static void ValidatePartialSealedStatic_02(CSharpCompilation compilation1) + { + compilation1.VerifyDiagnostics(); + + var i1 = compilation1.GetTypeByMetadataName("I1"); + var m01 = i1.GetMember("M01"); + + Assert.False(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.False(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + Assert.True(m01.IsPartialDefinition()); + Assert.Same(m01, m01.PartialImplementationPart.PartialDefinitionPart); + + m01 = m01.PartialImplementationPart; + + Assert.False(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.False(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + Assert.True(m01.IsPartialImplementation()); + } + + [Fact] + public void PartialSealedStatic_03() + { + var source1 = +@" +partial interface I1 +{ + static partial void M01(); +} +partial interface I1 +{ + sealed static partial void M01() {} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + ValidatePartialSealedStatic_02(compilation1); + } + + [Fact] + public void PartialSealedStatic_04() + { + var source1 = +@" +partial interface I1 +{ + sealed static partial void M01(); +} +partial interface I1 +{ + static partial void M01() {} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + ValidatePartialSealedStatic_02(compilation1); + } + + [Fact] + public void PartialAbstractStatic_01() + { + var source1 = +@" +partial interface I1 +{ + abstract static partial void M01(); +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,34): error CS0750: A partial method cannot have the 'abstract' modifier + // abstract static partial void M01(); + Diagnostic(ErrorCode.ERR_PartialMethodInvalidModifier, "M01").WithLocation(4, 34) + ); + + var i1 = compilation1.GetTypeByMetadataName("I1"); + var m01 = i1.GetMember("M01"); + + Assert.True(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.True(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + Assert.True(m01.IsPartialDefinition()); + Assert.Null(m01.PartialImplementationPart); + } + + [Fact] + public void PartialAbstractStatic_02() + { + var source1 = +@" +partial interface I1 +{ + abstract static partial void M01(); +} +partial interface I1 +{ + abstract static partial void M01() {} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,34): error CS0750: A partial method cannot have the 'abstract' modifier + // abstract static partial void M01(); + Diagnostic(ErrorCode.ERR_PartialMethodInvalidModifier, "M01").WithLocation(4, 34), + // (8,34): error CS0500: 'I1.M01()' cannot declare a body because it is marked abstract + // abstract static partial void M01() {} + Diagnostic(ErrorCode.ERR_AbstractHasBody, "M01").WithArguments("I1.M01()").WithLocation(8, 34), + // (8,34): error CS0750: A partial method cannot have the 'abstract' modifier + // abstract static partial void M01() {} + Diagnostic(ErrorCode.ERR_PartialMethodInvalidModifier, "M01").WithLocation(8, 34) + ); + + var i1 = compilation1.GetTypeByMetadataName("I1"); + var m01 = i1.GetMember("M01"); + + Assert.True(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.True(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + Assert.True(m01.IsPartialDefinition()); + Assert.Same(m01, m01.PartialImplementationPart.PartialDefinitionPart); + + m01 = m01.PartialImplementationPart; + + Assert.True(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.True(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + Assert.True(m01.IsPartialImplementation()); + } + + [Fact] + public void PartialAbstractStatic_03() + { + var source1 = +@" +partial interface I1 +{ + abstract static partial void M01(); +} +partial interface I1 +{ + static partial void M01() {} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,34): error CS0750: A partial method cannot have the 'abstract' modifier + // abstract static partial void M01(); + Diagnostic(ErrorCode.ERR_PartialMethodInvalidModifier, "M01").WithLocation(4, 34) + ); + + var i1 = compilation1.GetTypeByMetadataName("I1"); + var m01 = i1.GetMember("M01"); + + Assert.True(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.True(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + Assert.True(m01.IsPartialDefinition()); + Assert.Same(m01, m01.PartialImplementationPart.PartialDefinitionPart); + + m01 = m01.PartialImplementationPart; + + Assert.False(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.False(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + Assert.True(m01.IsPartialImplementation()); + } + + [Fact] + public void PartialAbstractStatic_04() + { + var source1 = +@" +partial interface I1 +{ + static partial void M01(); +} +partial interface I1 +{ + abstract static partial void M01() {} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (8,34): error CS0500: 'I1.M01()' cannot declare a body because it is marked abstract + // abstract static partial void M01() {} + Diagnostic(ErrorCode.ERR_AbstractHasBody, "M01").WithArguments("I1.M01()").WithLocation(8, 34), + // (8,34): error CS0750: A partial method cannot have the 'abstract' modifier + // abstract static partial void M01() {} + Diagnostic(ErrorCode.ERR_PartialMethodInvalidModifier, "M01").WithLocation(8, 34) + ); + + var i1 = compilation1.GetTypeByMetadataName("I1"); + var m01 = i1.GetMember("M01"); + + Assert.False(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.False(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + Assert.True(m01.IsPartialDefinition()); + Assert.Same(m01, m01.PartialImplementationPart.PartialDefinitionPart); + + m01 = m01.PartialImplementationPart; + + Assert.True(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.True(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + Assert.True(m01.IsPartialImplementation()); + } + + [Fact] + public void PrivateAbstractStatic_01() + { + var source1 = +@" +interface I1 +{ + private abstract static void M01(); + private abstract static bool P01 { get; } + private abstract static event System.Action E01; + private abstract static I1 operator+ (I1 x); +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,34): error CS0621: 'I1.M01()': virtual or abstract members cannot be private + // private abstract static void M01(); + Diagnostic(ErrorCode.ERR_VirtualPrivate, "M01").WithArguments("I1.M01()").WithLocation(4, 34), + // (5,34): error CS0621: 'I1.P01': virtual or abstract members cannot be private + // private abstract static bool P01 { get; } + Diagnostic(ErrorCode.ERR_VirtualPrivate, "P01").WithArguments("I1.P01").WithLocation(5, 34), + // (6,49): error CS0621: 'I1.E01': virtual or abstract members cannot be private + // private abstract static event System.Action E01; + Diagnostic(ErrorCode.ERR_VirtualPrivate, "E01").WithArguments("I1.E01").WithLocation(6, 49), + // (7,40): error CS0558: User-defined operator 'I1.operator +(I1)' must be declared static and public + // private abstract static I1 operator+ (I1 x); + Diagnostic(ErrorCode.ERR_OperatorsMustBeStatic, "+").WithArguments("I1.operator +(I1)").WithLocation(7, 40) + ); + } + + [Fact] + public void PropertyModifiers_01() + { + var source1 = +@" +public interface I1 +{ + abstract static bool M01 { get + ; } + + virtual static bool M02 { get + ; } + + sealed static bool M03 { get + ; } + + override static bool M04 { get + ; } + + abstract virtual static bool M05 { get + ; } + + abstract sealed static bool M06 { get + ; } + + abstract override static bool M07 { get + ; } + + virtual sealed static bool M08 { get + ; } + + virtual override static bool M09 { get + ; } + + sealed override static bool M10 { get + ; } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,26): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static bool M01 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "9.0", "preview").WithLocation(4, 26), + // (7,25): error CS0112: A static member cannot be marked as 'virtual' + // virtual static bool M02 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M02").WithArguments("virtual").WithLocation(7, 25), + // (10,24): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // sealed static bool M03 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M03").WithArguments("sealed", "9.0", "preview").WithLocation(10, 24), + // (13,26): error CS0106: The modifier 'override' is not valid for this item + // override static bool M04 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M04").WithArguments("override").WithLocation(13, 26), + // (16,34): error CS0112: A static member cannot be marked as 'virtual' + // abstract virtual static bool M05 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M05").WithArguments("virtual").WithLocation(16, 34), + // (16,34): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract virtual static bool M05 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M05").WithArguments("abstract", "9.0", "preview").WithLocation(16, 34), + // (19,33): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static bool M06 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M06").WithArguments("sealed").WithLocation(19, 33), + // (19,33): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract sealed static bool M06 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M06").WithArguments("abstract", "9.0", "preview").WithLocation(19, 33), + // (22,35): error CS0106: The modifier 'override' is not valid for this item + // abstract override static bool M07 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M07").WithArguments("override").WithLocation(22, 35), + // (22,35): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract override static bool M07 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M07").WithArguments("abstract", "9.0", "preview").WithLocation(22, 35), + // (25,32): error CS0112: A static member cannot be marked as 'virtual' + // virtual sealed static bool M08 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M08").WithArguments("virtual").WithLocation(25, 32), + // (25,32): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // virtual sealed static bool M08 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M08").WithArguments("sealed", "9.0", "preview").WithLocation(25, 32), + // (28,34): error CS0112: A static member cannot be marked as 'virtual' + // virtual override static bool M09 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M09").WithArguments("virtual").WithLocation(28, 34), + // (28,34): error CS0106: The modifier 'override' is not valid for this item + // virtual override static bool M09 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M09").WithArguments("override").WithLocation(28, 34), + // (31,33): error CS0106: The modifier 'override' is not valid for this item + // sealed override static bool M10 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M10").WithArguments("override").WithLocation(31, 33), + // (31,33): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // sealed override static bool M10 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M10").WithArguments("sealed", "9.0", "preview").WithLocation(31, 33) + ); + + ValidatePropertyModifiers_01(compilation1); + } + + private static void ValidatePropertyModifiers_01(CSharpCompilation compilation1) + { + var i1 = compilation1.GetTypeByMetadataName("I1"); + + { + var m01 = i1.GetMember("M01"); + + Assert.True(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + var m02 = i1.GetMember("M02"); + + Assert.False(m02.IsAbstract); + Assert.False(m02.IsVirtual); + Assert.False(m02.IsSealed); + Assert.True(m02.IsStatic); + Assert.False(m02.IsExtern); + Assert.False(m02.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m02)); + + var m03 = i1.GetMember("M03"); + + Assert.False(m03.IsAbstract); + Assert.False(m03.IsVirtual); + Assert.False(m03.IsSealed); + Assert.True(m03.IsStatic); + Assert.False(m03.IsExtern); + Assert.False(m03.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m03)); + + var m04 = i1.GetMember("M04"); + + Assert.False(m04.IsAbstract); + Assert.False(m04.IsVirtual); + Assert.False(m04.IsSealed); + Assert.True(m04.IsStatic); + Assert.False(m04.IsExtern); + Assert.False(m04.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m04)); + + var m05 = i1.GetMember("M05"); + + Assert.True(m05.IsAbstract); + Assert.False(m05.IsVirtual); + Assert.False(m05.IsSealed); + Assert.True(m05.IsStatic); + Assert.False(m05.IsExtern); + Assert.False(m05.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m05)); + + var m06 = i1.GetMember("M06"); + + Assert.True(m06.IsAbstract); + Assert.False(m06.IsVirtual); + Assert.False(m06.IsSealed); + Assert.True(m06.IsStatic); + Assert.False(m06.IsExtern); + Assert.False(m06.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m06)); + + var m07 = i1.GetMember("M07"); + + Assert.True(m07.IsAbstract); + Assert.False(m07.IsVirtual); + Assert.False(m07.IsSealed); + Assert.True(m07.IsStatic); + Assert.False(m07.IsExtern); + Assert.False(m07.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m07)); + + var m08 = i1.GetMember("M08"); + + Assert.False(m08.IsAbstract); + Assert.False(m08.IsVirtual); + Assert.False(m08.IsSealed); + Assert.True(m08.IsStatic); + Assert.False(m08.IsExtern); + Assert.False(m08.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m08)); + + var m09 = i1.GetMember("M09"); + + Assert.False(m09.IsAbstract); + Assert.False(m09.IsVirtual); + Assert.False(m09.IsSealed); + Assert.True(m09.IsStatic); + Assert.False(m09.IsExtern); + Assert.False(m09.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m09)); + + var m10 = i1.GetMember("M10"); + + Assert.False(m10.IsAbstract); + Assert.False(m10.IsVirtual); + Assert.False(m10.IsSealed); + Assert.True(m10.IsStatic); + Assert.False(m10.IsExtern); + Assert.False(m10.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m10)); + } + { + var m01 = i1.GetMember("M01").GetMethod; + + Assert.True(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.True(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + var m02 = i1.GetMember("M02").GetMethod; + + Assert.False(m02.IsAbstract); + Assert.False(m02.IsVirtual); + Assert.False(m02.IsMetadataVirtual()); + Assert.False(m02.IsSealed); + Assert.True(m02.IsStatic); + Assert.False(m02.IsExtern); + Assert.False(m02.IsAsync); + Assert.False(m02.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m02)); + + var m03 = i1.GetMember("M03").GetMethod; + + Assert.False(m03.IsAbstract); + Assert.False(m03.IsVirtual); + Assert.False(m03.IsMetadataVirtual()); + Assert.False(m03.IsSealed); + Assert.True(m03.IsStatic); + Assert.False(m03.IsExtern); + Assert.False(m03.IsAsync); + Assert.False(m03.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m03)); + + var m04 = i1.GetMember("M04").GetMethod; + + Assert.False(m04.IsAbstract); + Assert.False(m04.IsVirtual); + Assert.False(m04.IsMetadataVirtual()); + Assert.False(m04.IsSealed); + Assert.True(m04.IsStatic); + Assert.False(m04.IsExtern); + Assert.False(m04.IsAsync); + Assert.False(m04.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m04)); + + var m05 = i1.GetMember("M05").GetMethod; + + Assert.True(m05.IsAbstract); + Assert.False(m05.IsVirtual); + Assert.True(m05.IsMetadataVirtual()); + Assert.False(m05.IsSealed); + Assert.True(m05.IsStatic); + Assert.False(m05.IsExtern); + Assert.False(m05.IsAsync); + Assert.False(m05.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m05)); + + var m06 = i1.GetMember("M06").GetMethod; + + Assert.True(m06.IsAbstract); + Assert.False(m06.IsVirtual); + Assert.True(m06.IsMetadataVirtual()); + Assert.False(m06.IsSealed); + Assert.True(m06.IsStatic); + Assert.False(m06.IsExtern); + Assert.False(m06.IsAsync); + Assert.False(m06.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m06)); + + var m07 = i1.GetMember("M07").GetMethod; + + Assert.True(m07.IsAbstract); + Assert.False(m07.IsVirtual); + Assert.True(m07.IsMetadataVirtual()); + Assert.False(m07.IsSealed); + Assert.True(m07.IsStatic); + Assert.False(m07.IsExtern); + Assert.False(m07.IsAsync); + Assert.False(m07.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m07)); + + var m08 = i1.GetMember("M08").GetMethod; + + Assert.False(m08.IsAbstract); + Assert.False(m08.IsVirtual); + Assert.False(m08.IsMetadataVirtual()); + Assert.False(m08.IsSealed); + Assert.True(m08.IsStatic); + Assert.False(m08.IsExtern); + Assert.False(m08.IsAsync); + Assert.False(m08.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m08)); + + var m09 = i1.GetMember("M09").GetMethod; + + Assert.False(m09.IsAbstract); + Assert.False(m09.IsVirtual); + Assert.False(m09.IsMetadataVirtual()); + Assert.False(m09.IsSealed); + Assert.True(m09.IsStatic); + Assert.False(m09.IsExtern); + Assert.False(m09.IsAsync); + Assert.False(m09.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m09)); + + var m10 = i1.GetMember("M10").GetMethod; + + Assert.False(m10.IsAbstract); + Assert.False(m10.IsVirtual); + Assert.False(m10.IsMetadataVirtual()); + Assert.False(m10.IsSealed); + Assert.True(m10.IsStatic); + Assert.False(m10.IsExtern); + Assert.False(m10.IsAsync); + Assert.False(m10.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m10)); + } + } + + [Fact] + public void PropertyModifiers_02() + { + var source1 = +@" +public interface I1 +{ + abstract static bool M01 { get + => throw null; } + + virtual static bool M02 { get + => throw null; } + + sealed static bool M03 { get + => throw null; } + + override static bool M04 { get + => throw null; } + + abstract virtual static bool M05 { get + { throw null; } } + + abstract sealed static bool M06 { get + => throw null; } + + abstract override static bool M07 { get + => throw null; } + + virtual sealed static bool M08 { get + => throw null; } + + virtual override static bool M09 { get + => throw null; } + + sealed override static bool M10 { get + => throw null; } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,26): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static bool M01 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "9.0", "preview").WithLocation(4, 26), + // (4,32): error CS0500: 'I1.M01.get' cannot declare a body because it is marked abstract + // abstract static bool M01 { get + Diagnostic(ErrorCode.ERR_AbstractHasBody, "get").WithArguments("I1.M01.get").WithLocation(4, 32), + // (7,25): error CS0112: A static member cannot be marked as 'virtual' + // virtual static bool M02 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M02").WithArguments("virtual").WithLocation(7, 25), + // (10,24): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // sealed static bool M03 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M03").WithArguments("sealed", "9.0", "preview").WithLocation(10, 24), + // (13,26): error CS0106: The modifier 'override' is not valid for this item + // override static bool M04 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M04").WithArguments("override").WithLocation(13, 26), + // (16,34): error CS0112: A static member cannot be marked as 'virtual' + // abstract virtual static bool M05 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M05").WithArguments("virtual").WithLocation(16, 34), + // (16,34): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract virtual static bool M05 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M05").WithArguments("abstract", "9.0", "preview").WithLocation(16, 34), + // (16,40): error CS0500: 'I1.M05.get' cannot declare a body because it is marked abstract + // abstract virtual static bool M05 { get + Diagnostic(ErrorCode.ERR_AbstractHasBody, "get").WithArguments("I1.M05.get").WithLocation(16, 40), + // (19,33): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static bool M06 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M06").WithArguments("sealed").WithLocation(19, 33), + // (19,33): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract sealed static bool M06 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M06").WithArguments("abstract", "9.0", "preview").WithLocation(19, 33), + // (19,39): error CS0500: 'I1.M06.get' cannot declare a body because it is marked abstract + // abstract sealed static bool M06 { get + Diagnostic(ErrorCode.ERR_AbstractHasBody, "get").WithArguments("I1.M06.get").WithLocation(19, 39), + // (22,35): error CS0106: The modifier 'override' is not valid for this item + // abstract override static bool M07 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M07").WithArguments("override").WithLocation(22, 35), + // (22,35): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract override static bool M07 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M07").WithArguments("abstract", "9.0", "preview").WithLocation(22, 35), + // (22,41): error CS0500: 'I1.M07.get' cannot declare a body because it is marked abstract + // abstract override static bool M07 { get + Diagnostic(ErrorCode.ERR_AbstractHasBody, "get").WithArguments("I1.M07.get").WithLocation(22, 41), + // (25,32): error CS0112: A static member cannot be marked as 'virtual' + // virtual sealed static bool M08 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M08").WithArguments("virtual").WithLocation(25, 32), + // (25,32): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // virtual sealed static bool M08 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M08").WithArguments("sealed", "9.0", "preview").WithLocation(25, 32), + // (28,34): error CS0112: A static member cannot be marked as 'virtual' + // virtual override static bool M09 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M09").WithArguments("virtual").WithLocation(28, 34), + // (28,34): error CS0106: The modifier 'override' is not valid for this item + // virtual override static bool M09 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M09").WithArguments("override").WithLocation(28, 34), + // (31,33): error CS0106: The modifier 'override' is not valid for this item + // sealed override static bool M10 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M10").WithArguments("override").WithLocation(31, 33), + // (31,33): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // sealed override static bool M10 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M10").WithArguments("sealed", "9.0", "preview").WithLocation(31, 33) + ); + + ValidatePropertyModifiers_01(compilation1); + } + + [Fact] + public void PropertyModifiers_03() + { + var source1 = +@" +public interface I1 +{ + abstract static bool M01 { get + ; } + + virtual static bool M02 { get + ; } + + sealed static bool M03 { get + ; } + + override static bool M04 { get + ; } + + abstract virtual static bool M05 { get + ; } + + abstract sealed static bool M06 { get + ; } + + abstract override static bool M07 { get + ; } + + virtual sealed static bool M08 { get + ; } + + virtual override static bool M09 { get + ; } + + sealed override static bool M10 { get + ; } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (7,25): error CS0112: A static member cannot be marked as 'virtual' + // virtual static bool M02 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M02").WithArguments("virtual").WithLocation(7, 25), + // (13,26): error CS0106: The modifier 'override' is not valid for this item + // override static bool M04 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M04").WithArguments("override").WithLocation(13, 26), + // (16,34): error CS0112: A static member cannot be marked as 'virtual' + // abstract virtual static bool M05 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M05").WithArguments("virtual").WithLocation(16, 34), + // (19,33): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static bool M06 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M06").WithArguments("sealed").WithLocation(19, 33), + // (22,35): error CS0106: The modifier 'override' is not valid for this item + // abstract override static bool M07 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M07").WithArguments("override").WithLocation(22, 35), + // (25,32): error CS0112: A static member cannot be marked as 'virtual' + // virtual sealed static bool M08 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M08").WithArguments("virtual").WithLocation(25, 32), + // (28,34): error CS0112: A static member cannot be marked as 'virtual' + // virtual override static bool M09 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M09").WithArguments("virtual").WithLocation(28, 34), + // (28,34): error CS0106: The modifier 'override' is not valid for this item + // virtual override static bool M09 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M09").WithArguments("override").WithLocation(28, 34), + // (31,33): error CS0106: The modifier 'override' is not valid for this item + // sealed override static bool M10 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M10").WithArguments("override").WithLocation(31, 33) + ); + + ValidatePropertyModifiers_01(compilation1); + } + + [Fact] + public void PropertyModifiers_04() + { + var source1 = +@" +public interface I1 +{ + abstract static bool M01 { get + => throw null; } + + virtual static bool M02 { get + => throw null; } + + sealed static bool M03 { get + => throw null; } + + override static bool M04 { get + => throw null; } + + abstract virtual static bool M05 { get + { throw null; } } + + abstract sealed static bool M06 { get + => throw null; } + + abstract override static bool M07 { get + => throw null; } + + virtual sealed static bool M08 { get + => throw null; } + + virtual override static bool M09 { get + => throw null; } + + sealed override static bool M10 { get + => throw null; } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,32): error CS0500: 'I1.M01.get' cannot declare a body because it is marked abstract + // abstract static bool M01 { get + Diagnostic(ErrorCode.ERR_AbstractHasBody, "get").WithArguments("I1.M01.get").WithLocation(4, 32), + // (7,25): error CS0112: A static member cannot be marked as 'virtual' + // virtual static bool M02 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M02").WithArguments("virtual").WithLocation(7, 25), + // (13,26): error CS0106: The modifier 'override' is not valid for this item + // override static bool M04 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M04").WithArguments("override").WithLocation(13, 26), + // (16,34): error CS0112: A static member cannot be marked as 'virtual' + // abstract virtual static bool M05 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M05").WithArguments("virtual").WithLocation(16, 34), + // (16,40): error CS0500: 'I1.M05.get' cannot declare a body because it is marked abstract + // abstract virtual static bool M05 { get + Diagnostic(ErrorCode.ERR_AbstractHasBody, "get").WithArguments("I1.M05.get").WithLocation(16, 40), + // (19,33): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static bool M06 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M06").WithArguments("sealed").WithLocation(19, 33), + // (19,39): error CS0500: 'I1.M06.get' cannot declare a body because it is marked abstract + // abstract sealed static bool M06 { get + Diagnostic(ErrorCode.ERR_AbstractHasBody, "get").WithArguments("I1.M06.get").WithLocation(19, 39), + // (22,35): error CS0106: The modifier 'override' is not valid for this item + // abstract override static bool M07 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M07").WithArguments("override").WithLocation(22, 35), + // (22,41): error CS0500: 'I1.M07.get' cannot declare a body because it is marked abstract + // abstract override static bool M07 { get + Diagnostic(ErrorCode.ERR_AbstractHasBody, "get").WithArguments("I1.M07.get").WithLocation(22, 41), + // (25,32): error CS0112: A static member cannot be marked as 'virtual' + // virtual sealed static bool M08 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M08").WithArguments("virtual").WithLocation(25, 32), + // (28,34): error CS0112: A static member cannot be marked as 'virtual' + // virtual override static bool M09 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M09").WithArguments("virtual").WithLocation(28, 34), + // (28,34): error CS0106: The modifier 'override' is not valid for this item + // virtual override static bool M09 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M09").WithArguments("override").WithLocation(28, 34), + // (31,33): error CS0106: The modifier 'override' is not valid for this item + // sealed override static bool M10 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M10").WithArguments("override").WithLocation(31, 33) + ); + + ValidatePropertyModifiers_01(compilation1); + } + + [Fact] + public void PropertyModifiers_05() + { + var source1 = +@" +public interface I1 +{ + abstract static bool M01 { get + ; } + + virtual static bool M02 { get + ; } + + sealed static bool M03 { get + ; } + + override static bool M04 { get + ; } + + abstract virtual static bool M05 { get + ; } + + abstract sealed static bool M06 { get + ; } + + abstract override static bool M07 { get + ; } + + virtual sealed static bool M08 { get + ; } + + virtual override static bool M09 { get + ; } + + sealed override static bool M10 { get + ; } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular7_3, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,26): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract static bool M01 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "7.3", "preview").WithLocation(4, 26), + // (7,25): error CS0112: A static member cannot be marked as 'virtual' + // virtual static bool M02 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M02").WithArguments("virtual").WithLocation(7, 25), + // (7,25): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. + // virtual static bool M02 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M02").WithArguments("static", "7.3", "8.0").WithLocation(7, 25), + // (10,24): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // sealed static bool M03 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M03").WithArguments("sealed", "7.3", "preview").WithLocation(10, 24), + // (13,26): error CS0106: The modifier 'override' is not valid for this item + // override static bool M04 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M04").WithArguments("override").WithLocation(13, 26), + // (13,26): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. + // override static bool M04 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M04").WithArguments("static", "7.3", "8.0").WithLocation(13, 26), + // (16,34): error CS0112: A static member cannot be marked as 'virtual' + // abstract virtual static bool M05 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M05").WithArguments("virtual").WithLocation(16, 34), + // (16,34): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract virtual static bool M05 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M05").WithArguments("abstract", "7.3", "preview").WithLocation(16, 34), + // (19,33): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static bool M06 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M06").WithArguments("sealed").WithLocation(19, 33), + // (19,33): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract sealed static bool M06 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M06").WithArguments("abstract", "7.3", "preview").WithLocation(19, 33), + // (22,35): error CS0106: The modifier 'override' is not valid for this item + // abstract override static bool M07 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M07").WithArguments("override").WithLocation(22, 35), + // (22,35): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract override static bool M07 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M07").WithArguments("abstract", "7.3", "preview").WithLocation(22, 35), + // (25,32): error CS0112: A static member cannot be marked as 'virtual' + // virtual sealed static bool M08 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M08").WithArguments("virtual").WithLocation(25, 32), + // (25,32): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // virtual sealed static bool M08 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M08").WithArguments("sealed", "7.3", "preview").WithLocation(25, 32), + // (28,34): error CS0112: A static member cannot be marked as 'virtual' + // virtual override static bool M09 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M09").WithArguments("virtual").WithLocation(28, 34), + // (28,34): error CS0106: The modifier 'override' is not valid for this item + // virtual override static bool M09 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M09").WithArguments("override").WithLocation(28, 34), + // (28,34): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. + // virtual override static bool M09 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M09").WithArguments("static", "7.3", "8.0").WithLocation(28, 34), + // (31,33): error CS0106: The modifier 'override' is not valid for this item + // sealed override static bool M10 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M10").WithArguments("override").WithLocation(31, 33), + // (31,33): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // sealed override static bool M10 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M10").WithArguments("sealed", "7.3", "preview").WithLocation(31, 33) + ); + + ValidatePropertyModifiers_01(compilation1); + } + + [Fact] + public void PropertyModifiers_06() + { + var source1 = +@" +public interface I1 +{ + abstract static bool M01 { get + => throw null; } + + virtual static bool M02 { get + => throw null; } + + sealed static bool M03 { get + => throw null; } + + override static bool M04 { get + => throw null; } + + abstract virtual static bool M05 { get + { throw null; } } + + abstract sealed static bool M06 { get + => throw null; } + + abstract override static bool M07 { get + => throw null; } + + virtual sealed static bool M08 { get + => throw null; } + + virtual override static bool M09 { get + => throw null; } + + sealed override static bool M10 { get + => throw null; } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular7_3, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,26): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract static bool M01 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "7.3", "preview").WithLocation(4, 26), + // (4,32): error CS0500: 'I1.M01.get' cannot declare a body because it is marked abstract + // abstract static bool M01 { get + Diagnostic(ErrorCode.ERR_AbstractHasBody, "get").WithArguments("I1.M01.get").WithLocation(4, 32), + // (7,25): error CS0112: A static member cannot be marked as 'virtual' + // virtual static bool M02 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M02").WithArguments("virtual").WithLocation(7, 25), + // (7,25): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + // virtual static bool M02 { get + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "M02").WithArguments("default interface implementation", "8.0").WithLocation(7, 25), + // (10,24): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // sealed static bool M03 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M03").WithArguments("sealed", "7.3", "preview").WithLocation(10, 24), + // (13,26): error CS0106: The modifier 'override' is not valid for this item + // override static bool M04 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M04").WithArguments("override").WithLocation(13, 26), + // (13,26): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + // override static bool M04 { get + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "M04").WithArguments("default interface implementation", "8.0").WithLocation(13, 26), + // (16,34): error CS0112: A static member cannot be marked as 'virtual' + // abstract virtual static bool M05 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M05").WithArguments("virtual").WithLocation(16, 34), + // (16,34): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract virtual static bool M05 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M05").WithArguments("abstract", "7.3", "preview").WithLocation(16, 34), + // (16,40): error CS0500: 'I1.M05.get' cannot declare a body because it is marked abstract + // abstract virtual static bool M05 { get + Diagnostic(ErrorCode.ERR_AbstractHasBody, "get").WithArguments("I1.M05.get").WithLocation(16, 40), + // (19,33): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static bool M06 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M06").WithArguments("sealed").WithLocation(19, 33), + // (19,33): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract sealed static bool M06 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M06").WithArguments("abstract", "7.3", "preview").WithLocation(19, 33), + // (19,39): error CS0500: 'I1.M06.get' cannot declare a body because it is marked abstract + // abstract sealed static bool M06 { get + Diagnostic(ErrorCode.ERR_AbstractHasBody, "get").WithArguments("I1.M06.get").WithLocation(19, 39), + // (22,35): error CS0106: The modifier 'override' is not valid for this item + // abstract override static bool M07 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M07").WithArguments("override").WithLocation(22, 35), + // (22,35): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract override static bool M07 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M07").WithArguments("abstract", "7.3", "preview").WithLocation(22, 35), + // (22,41): error CS0500: 'I1.M07.get' cannot declare a body because it is marked abstract + // abstract override static bool M07 { get + Diagnostic(ErrorCode.ERR_AbstractHasBody, "get").WithArguments("I1.M07.get").WithLocation(22, 41), + // (25,32): error CS0112: A static member cannot be marked as 'virtual' + // virtual sealed static bool M08 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M08").WithArguments("virtual").WithLocation(25, 32), + // (25,32): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // virtual sealed static bool M08 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M08").WithArguments("sealed", "7.3", "preview").WithLocation(25, 32), + // (28,34): error CS0112: A static member cannot be marked as 'virtual' + // virtual override static bool M09 { get + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M09").WithArguments("virtual").WithLocation(28, 34), + // (28,34): error CS0106: The modifier 'override' is not valid for this item + // virtual override static bool M09 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M09").WithArguments("override").WithLocation(28, 34), + // (28,34): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + // virtual override static bool M09 { get + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "M09").WithArguments("default interface implementation", "8.0").WithLocation(28, 34), + // (31,33): error CS0106: The modifier 'override' is not valid for this item + // sealed override static bool M10 { get + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M10").WithArguments("override").WithLocation(31, 33), + // (31,33): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // sealed override static bool M10 { get + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M10").WithArguments("sealed", "7.3", "preview").WithLocation(31, 33) + ); + + ValidatePropertyModifiers_01(compilation1); + } + + [Fact] + public void EventModifiers_01() + { + var source1 = +@"#pragma warning disable CS0067 // The event is never used +public interface I1 +{ + abstract static event D M01 + ; + + virtual static event D M02 + ; + + sealed static event D M03 + ; + + override static event D M04 + ; + + abstract virtual static event D M05 + ; + + abstract sealed static event D M06 + ; + + abstract override static event D M07 + ; + + virtual sealed static event D M08 + ; + + virtual override static event D M09 + ; + + sealed override static event D M10 + ; +} + +public delegate void D(); +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,29): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static event D M01 + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "9.0", "preview").WithLocation(4, 29), + // (7,28): error CS0112: A static member cannot be marked as 'virtual' + // virtual static event D M02 + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M02").WithArguments("virtual").WithLocation(7, 28), + // (10,27): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // sealed static event D M03 + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M03").WithArguments("sealed", "9.0", "preview").WithLocation(10, 27), + // (13,29): error CS0106: The modifier 'override' is not valid for this item + // override static event D M04 + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M04").WithArguments("override").WithLocation(13, 29), + // (16,37): error CS0112: A static member cannot be marked as 'virtual' + // abstract virtual static event D M05 + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M05").WithArguments("virtual").WithLocation(16, 37), + // (16,37): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract virtual static event D M05 + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M05").WithArguments("abstract", "9.0", "preview").WithLocation(16, 37), + // (19,36): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static event D M06 + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M06").WithArguments("sealed").WithLocation(19, 36), + // (19,36): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract sealed static event D M06 + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M06").WithArguments("abstract", "9.0", "preview").WithLocation(19, 36), + // (22,38): error CS0106: The modifier 'override' is not valid for this item + // abstract override static event D M07 + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M07").WithArguments("override").WithLocation(22, 38), + // (22,38): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract override static event D M07 + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M07").WithArguments("abstract", "9.0", "preview").WithLocation(22, 38), + // (25,35): error CS0112: A static member cannot be marked as 'virtual' + // virtual sealed static event D M08 + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M08").WithArguments("virtual").WithLocation(25, 35), + // (25,35): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // virtual sealed static event D M08 + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M08").WithArguments("sealed", "9.0", "preview").WithLocation(25, 35), + // (28,37): error CS0112: A static member cannot be marked as 'virtual' + // virtual override static event D M09 + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M09").WithArguments("virtual").WithLocation(28, 37), + // (28,37): error CS0106: The modifier 'override' is not valid for this item + // virtual override static event D M09 + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M09").WithArguments("override").WithLocation(28, 37), + // (31,36): error CS0106: The modifier 'override' is not valid for this item + // sealed override static event D M10 + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M10").WithArguments("override").WithLocation(31, 36), + // (31,36): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // sealed override static event D M10 + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M10").WithArguments("sealed", "9.0", "preview").WithLocation(31, 36) + ); + + ValidateEventModifiers_01(compilation1); + } + + private static void ValidateEventModifiers_01(CSharpCompilation compilation1) + { + var i1 = compilation1.GetTypeByMetadataName("I1"); + + { + var m01 = i1.GetMember("M01"); + + Assert.True(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + var m02 = i1.GetMember("M02"); + + Assert.False(m02.IsAbstract); + Assert.False(m02.IsVirtual); + Assert.False(m02.IsSealed); + Assert.True(m02.IsStatic); + Assert.False(m02.IsExtern); + Assert.False(m02.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m02)); + + var m03 = i1.GetMember("M03"); + + Assert.False(m03.IsAbstract); + Assert.False(m03.IsVirtual); + Assert.False(m03.IsSealed); + Assert.True(m03.IsStatic); + Assert.False(m03.IsExtern); + Assert.False(m03.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m03)); + + var m04 = i1.GetMember("M04"); + + Assert.False(m04.IsAbstract); + Assert.False(m04.IsVirtual); + Assert.False(m04.IsSealed); + Assert.True(m04.IsStatic); + Assert.False(m04.IsExtern); + Assert.False(m04.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m04)); + + var m05 = i1.GetMember("M05"); + + Assert.True(m05.IsAbstract); + Assert.False(m05.IsVirtual); + Assert.False(m05.IsSealed); + Assert.True(m05.IsStatic); + Assert.False(m05.IsExtern); + Assert.False(m05.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m05)); + + var m06 = i1.GetMember("M06"); + + Assert.True(m06.IsAbstract); + Assert.False(m06.IsVirtual); + Assert.False(m06.IsSealed); + Assert.True(m06.IsStatic); + Assert.False(m06.IsExtern); + Assert.False(m06.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m06)); + + var m07 = i1.GetMember("M07"); + + Assert.True(m07.IsAbstract); + Assert.False(m07.IsVirtual); + Assert.False(m07.IsSealed); + Assert.True(m07.IsStatic); + Assert.False(m07.IsExtern); + Assert.False(m07.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m07)); + + var m08 = i1.GetMember("M08"); + + Assert.False(m08.IsAbstract); + Assert.False(m08.IsVirtual); + Assert.False(m08.IsSealed); + Assert.True(m08.IsStatic); + Assert.False(m08.IsExtern); + Assert.False(m08.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m08)); + + var m09 = i1.GetMember("M09"); + + Assert.False(m09.IsAbstract); + Assert.False(m09.IsVirtual); + Assert.False(m09.IsSealed); + Assert.True(m09.IsStatic); + Assert.False(m09.IsExtern); + Assert.False(m09.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m09)); + + var m10 = i1.GetMember("M10"); + + Assert.False(m10.IsAbstract); + Assert.False(m10.IsVirtual); + Assert.False(m10.IsSealed); + Assert.True(m10.IsStatic); + Assert.False(m10.IsExtern); + Assert.False(m10.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m10)); + } + + foreach (var addAccessor in new[] { true, false }) + { + var m01 = getAccessor(i1.GetMember("M01"), addAccessor); + + Assert.True(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.True(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + var m02 = getAccessor(i1.GetMember("M02"), addAccessor); + + Assert.False(m02.IsAbstract); + Assert.False(m02.IsVirtual); + Assert.False(m02.IsMetadataVirtual()); + Assert.False(m02.IsSealed); + Assert.True(m02.IsStatic); + Assert.False(m02.IsExtern); + Assert.False(m02.IsAsync); + Assert.False(m02.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m02)); + + var m03 = getAccessor(i1.GetMember("M03"), addAccessor); + + Assert.False(m03.IsAbstract); + Assert.False(m03.IsVirtual); + Assert.False(m03.IsMetadataVirtual()); + Assert.False(m03.IsSealed); + Assert.True(m03.IsStatic); + Assert.False(m03.IsExtern); + Assert.False(m03.IsAsync); + Assert.False(m03.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m03)); + + var m04 = getAccessor(i1.GetMember("M04"), addAccessor); + + Assert.False(m04.IsAbstract); + Assert.False(m04.IsVirtual); + Assert.False(m04.IsMetadataVirtual()); + Assert.False(m04.IsSealed); + Assert.True(m04.IsStatic); + Assert.False(m04.IsExtern); + Assert.False(m04.IsAsync); + Assert.False(m04.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m04)); + + var m05 = getAccessor(i1.GetMember("M05"), addAccessor); + + Assert.True(m05.IsAbstract); + Assert.False(m05.IsVirtual); + Assert.True(m05.IsMetadataVirtual()); + Assert.False(m05.IsSealed); + Assert.True(m05.IsStatic); + Assert.False(m05.IsExtern); + Assert.False(m05.IsAsync); + Assert.False(m05.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m05)); + + var m06 = getAccessor(i1.GetMember("M06"), addAccessor); + + Assert.True(m06.IsAbstract); + Assert.False(m06.IsVirtual); + Assert.True(m06.IsMetadataVirtual()); + Assert.False(m06.IsSealed); + Assert.True(m06.IsStatic); + Assert.False(m06.IsExtern); + Assert.False(m06.IsAsync); + Assert.False(m06.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m06)); + + var m07 = getAccessor(i1.GetMember("M07"), addAccessor); + + Assert.True(m07.IsAbstract); + Assert.False(m07.IsVirtual); + Assert.True(m07.IsMetadataVirtual()); + Assert.False(m07.IsSealed); + Assert.True(m07.IsStatic); + Assert.False(m07.IsExtern); + Assert.False(m07.IsAsync); + Assert.False(m07.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m07)); + + var m08 = getAccessor(i1.GetMember("M08"), addAccessor); + + Assert.False(m08.IsAbstract); + Assert.False(m08.IsVirtual); + Assert.False(m08.IsMetadataVirtual()); + Assert.False(m08.IsSealed); + Assert.True(m08.IsStatic); + Assert.False(m08.IsExtern); + Assert.False(m08.IsAsync); + Assert.False(m08.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m08)); + + var m09 = getAccessor(i1.GetMember("M09"), addAccessor); + + Assert.False(m09.IsAbstract); + Assert.False(m09.IsVirtual); + Assert.False(m09.IsMetadataVirtual()); + Assert.False(m09.IsSealed); + Assert.True(m09.IsStatic); + Assert.False(m09.IsExtern); + Assert.False(m09.IsAsync); + Assert.False(m09.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m09)); + + var m10 = getAccessor(i1.GetMember("M10"), addAccessor); + + Assert.False(m10.IsAbstract); + Assert.False(m10.IsVirtual); + Assert.False(m10.IsMetadataVirtual()); + Assert.False(m10.IsSealed); + Assert.True(m10.IsStatic); + Assert.False(m10.IsExtern); + Assert.False(m10.IsAsync); + Assert.False(m10.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m10)); + } + + static MethodSymbol getAccessor(EventSymbol e, bool addAccessor) + { + return addAccessor ? e.AddMethod : e.RemoveMethod; + } + } + + [Fact] + public void EventModifiers_02() + { + var source1 = +@"#pragma warning disable CS0067 // The event is never used +public interface I1 +{ + abstract static event D M01 { add {} remove {} } + + + virtual static event D M02 { add {} remove {} } + + + sealed static event D M03 { add {} remove {} } + + + override static event D M04 { add {} remove {} } + + + abstract virtual static event D M05 { add {} remove {} } + + + abstract sealed static event D M06 { add {} remove {} } + + + abstract override static event D M07 { add {} remove {} } + + + virtual sealed static event D M08 { add {} remove {} } + + + virtual override static event D M09 { add {} remove {} } + + + sealed override static event D M10 { add {} remove {} } +} + +public delegate void D(); +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,29): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static event D M01 { add {} remove {} } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "9.0", "preview").WithLocation(4, 29), + // (4,33): error CS8712: 'I1.M01': abstract event cannot use event accessor syntax + // abstract static event D M01 { add {} remove {} } + Diagnostic(ErrorCode.ERR_AbstractEventHasAccessors, "{").WithArguments("I1.M01").WithLocation(4, 33), + // (7,28): error CS0112: A static member cannot be marked as 'virtual' + // virtual static event D M02 { add {} remove {} } + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M02").WithArguments("virtual").WithLocation(7, 28), + // (10,27): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // sealed static event D M03 { add {} remove {} } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M03").WithArguments("sealed", "9.0", "preview").WithLocation(10, 27), + // (13,29): error CS0106: The modifier 'override' is not valid for this item + // override static event D M04 { add {} remove {} } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M04").WithArguments("override").WithLocation(13, 29), + // (16,37): error CS0112: A static member cannot be marked as 'virtual' + // abstract virtual static event D M05 { add {} remove {} } + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M05").WithArguments("virtual").WithLocation(16, 37), + // (16,37): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract virtual static event D M05 { add {} remove {} } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M05").WithArguments("abstract", "9.0", "preview").WithLocation(16, 37), + // (16,41): error CS8712: 'I1.M05': abstract event cannot use event accessor syntax + // abstract virtual static event D M05 { add {} remove {} } + Diagnostic(ErrorCode.ERR_AbstractEventHasAccessors, "{").WithArguments("I1.M05").WithLocation(16, 41), + // (19,36): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static event D M06 { add {} remove {} } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M06").WithArguments("sealed").WithLocation(19, 36), + // (19,36): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract sealed static event D M06 { add {} remove {} } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M06").WithArguments("abstract", "9.0", "preview").WithLocation(19, 36), + // (19,40): error CS8712: 'I1.M06': abstract event cannot use event accessor syntax + // abstract sealed static event D M06 { add {} remove {} } + Diagnostic(ErrorCode.ERR_AbstractEventHasAccessors, "{").WithArguments("I1.M06").WithLocation(19, 40), + // (22,38): error CS0106: The modifier 'override' is not valid for this item + // abstract override static event D M07 { add {} remove {} } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M07").WithArguments("override").WithLocation(22, 38), + // (22,38): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract override static event D M07 { add {} remove {} } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M07").WithArguments("abstract", "9.0", "preview").WithLocation(22, 38), + // (22,42): error CS8712: 'I1.M07': abstract event cannot use event accessor syntax + // abstract override static event D M07 { add {} remove {} } + Diagnostic(ErrorCode.ERR_AbstractEventHasAccessors, "{").WithArguments("I1.M07").WithLocation(22, 42), + // (25,35): error CS0112: A static member cannot be marked as 'virtual' + // virtual sealed static event D M08 { add {} remove {} } + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M08").WithArguments("virtual").WithLocation(25, 35), + // (25,35): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // virtual sealed static event D M08 { add {} remove {} } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M08").WithArguments("sealed", "9.0", "preview").WithLocation(25, 35), + // (28,37): error CS0112: A static member cannot be marked as 'virtual' + // virtual override static event D M09 { add {} remove {} } + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M09").WithArguments("virtual").WithLocation(28, 37), + // (28,37): error CS0106: The modifier 'override' is not valid for this item + // virtual override static event D M09 { add {} remove {} } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M09").WithArguments("override").WithLocation(28, 37), + // (31,36): error CS0106: The modifier 'override' is not valid for this item + // sealed override static event D M10 { add {} remove {} } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M10").WithArguments("override").WithLocation(31, 36), + // (31,36): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // sealed override static event D M10 { add {} remove {} } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M10").WithArguments("sealed", "9.0", "preview").WithLocation(31, 36) + ); + + ValidateEventModifiers_01(compilation1); + } + + [Fact] + public void EventModifiers_03() + { + var source1 = +@"#pragma warning disable CS0067 // The event is never used +public interface I1 +{ + abstract static event D M01 + ; + + virtual static event D M02 + ; + + sealed static event D M03 + ; + + override static event D M04 + ; + + abstract virtual static event D M05 + ; + + abstract sealed static event D M06 + ; + + abstract override static event D M07 + ; + + virtual sealed static event D M08 + ; + + virtual override static event D M09 + ; + + sealed override static event D M10 + ; +} + +public delegate void D(); +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (7,28): error CS0112: A static member cannot be marked as 'virtual' + // virtual static event D M02 + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M02").WithArguments("virtual").WithLocation(7, 28), + // (13,29): error CS0106: The modifier 'override' is not valid for this item + // override static event D M04 + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M04").WithArguments("override").WithLocation(13, 29), + // (16,37): error CS0112: A static member cannot be marked as 'virtual' + // abstract virtual static event D M05 + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M05").WithArguments("virtual").WithLocation(16, 37), + // (19,36): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static event D M06 + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M06").WithArguments("sealed").WithLocation(19, 36), + // (22,38): error CS0106: The modifier 'override' is not valid for this item + // abstract override static event D M07 + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M07").WithArguments("override").WithLocation(22, 38), + // (25,35): error CS0112: A static member cannot be marked as 'virtual' + // virtual sealed static event D M08 + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M08").WithArguments("virtual").WithLocation(25, 35), + // (28,37): error CS0112: A static member cannot be marked as 'virtual' + // virtual override static event D M09 + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M09").WithArguments("virtual").WithLocation(28, 37), + // (28,37): error CS0106: The modifier 'override' is not valid for this item + // virtual override static event D M09 + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M09").WithArguments("override").WithLocation(28, 37), + // (31,36): error CS0106: The modifier 'override' is not valid for this item + // sealed override static event D M10 + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M10").WithArguments("override").WithLocation(31, 36) + ); + + ValidateEventModifiers_01(compilation1); + } + + [Fact] + public void EventModifiers_04() + { + var source1 = +@"#pragma warning disable CS0067 // The event is never used +public interface I1 +{ + abstract static event D M01 { add {} remove {} } + + + virtual static event D M02 { add {} remove {} } + + + sealed static event D M03 { add {} remove {} } + + + override static event D M04 { add {} remove {} } + + + abstract virtual static event D M05 { add {} remove {} } + + + abstract sealed static event D M06 { add {} remove {} } + + + abstract override static event D M07 { add {} remove {} } + + + virtual sealed static event D M08 { add {} remove {} } + + + virtual override static event D M09 { add {} remove {} } + + + sealed override static event D M10 { add {} remove {} } +} + +public delegate void D(); +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,33): error CS8712: 'I1.M01': abstract event cannot use event accessor syntax + // abstract static event D M01 { add {} remove {} } + Diagnostic(ErrorCode.ERR_AbstractEventHasAccessors, "{").WithArguments("I1.M01").WithLocation(4, 33), + // (7,28): error CS0112: A static member cannot be marked as 'virtual' + // virtual static event D M02 { add {} remove {} } + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M02").WithArguments("virtual").WithLocation(7, 28), + // (13,29): error CS0106: The modifier 'override' is not valid for this item + // override static event D M04 { add {} remove {} } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M04").WithArguments("override").WithLocation(13, 29), + // (16,37): error CS0112: A static member cannot be marked as 'virtual' + // abstract virtual static event D M05 { add {} remove {} } + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M05").WithArguments("virtual").WithLocation(16, 37), + // (16,41): error CS8712: 'I1.M05': abstract event cannot use event accessor syntax + // abstract virtual static event D M05 { add {} remove {} } + Diagnostic(ErrorCode.ERR_AbstractEventHasAccessors, "{").WithArguments("I1.M05").WithLocation(16, 41), + // (19,36): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static event D M06 { add {} remove {} } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M06").WithArguments("sealed").WithLocation(19, 36), + // (19,40): error CS8712: 'I1.M06': abstract event cannot use event accessor syntax + // abstract sealed static event D M06 { add {} remove {} } + Diagnostic(ErrorCode.ERR_AbstractEventHasAccessors, "{").WithArguments("I1.M06").WithLocation(19, 40), + // (22,38): error CS0106: The modifier 'override' is not valid for this item + // abstract override static event D M07 { add {} remove {} } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M07").WithArguments("override").WithLocation(22, 38), + // (22,42): error CS8712: 'I1.M07': abstract event cannot use event accessor syntax + // abstract override static event D M07 { add {} remove {} } + Diagnostic(ErrorCode.ERR_AbstractEventHasAccessors, "{").WithArguments("I1.M07").WithLocation(22, 42), + // (25,35): error CS0112: A static member cannot be marked as 'virtual' + // virtual sealed static event D M08 { add {} remove {} } + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M08").WithArguments("virtual").WithLocation(25, 35), + // (28,37): error CS0112: A static member cannot be marked as 'virtual' + // virtual override static event D M09 { add {} remove {} } + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M09").WithArguments("virtual").WithLocation(28, 37), + // (28,37): error CS0106: The modifier 'override' is not valid for this item + // virtual override static event D M09 { add {} remove {} } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M09").WithArguments("override").WithLocation(28, 37), + // (31,36): error CS0106: The modifier 'override' is not valid for this item + // sealed override static event D M10 { add {} remove {} } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M10").WithArguments("override").WithLocation(31, 36) + ); + + ValidateEventModifiers_01(compilation1); + } + + [Fact] + public void EventModifiers_05() + { + var source1 = +@"#pragma warning disable CS0067 // The event is never used +public interface I1 +{ + abstract static event D M01 + ; + + virtual static event D M02 + ; + + sealed static event D M03 + ; + + override static event D M04 + ; + + abstract virtual static event D M05 + ; + + abstract sealed static event D M06 + ; + + abstract override static event D M07 + ; + + virtual sealed static event D M08 + ; + + virtual override static event D M09 + ; + + sealed override static event D M10 + ; +} + +public delegate void D(); +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular7_3, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,29): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract static event D M01 + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "7.3", "preview").WithLocation(4, 29), + // (7,28): error CS0112: A static member cannot be marked as 'virtual' + // virtual static event D M02 + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M02").WithArguments("virtual").WithLocation(7, 28), + // (7,28): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. + // virtual static event D M02 + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M02").WithArguments("static", "7.3", "8.0").WithLocation(7, 28), + // (10,27): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // sealed static event D M03 + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M03").WithArguments("sealed", "7.3", "preview").WithLocation(10, 27), + // (13,29): error CS0106: The modifier 'override' is not valid for this item + // override static event D M04 + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M04").WithArguments("override").WithLocation(13, 29), + // (13,29): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. + // override static event D M04 + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M04").WithArguments("static", "7.3", "8.0").WithLocation(13, 29), + // (16,37): error CS0112: A static member cannot be marked as 'virtual' + // abstract virtual static event D M05 + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M05").WithArguments("virtual").WithLocation(16, 37), + // (16,37): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract virtual static event D M05 + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M05").WithArguments("abstract", "7.3", "preview").WithLocation(16, 37), + // (19,36): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static event D M06 + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M06").WithArguments("sealed").WithLocation(19, 36), + // (19,36): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract sealed static event D M06 + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M06").WithArguments("abstract", "7.3", "preview").WithLocation(19, 36), + // (22,38): error CS0106: The modifier 'override' is not valid for this item + // abstract override static event D M07 + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M07").WithArguments("override").WithLocation(22, 38), + // (22,38): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract override static event D M07 + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M07").WithArguments("abstract", "7.3", "preview").WithLocation(22, 38), + // (25,35): error CS0112: A static member cannot be marked as 'virtual' + // virtual sealed static event D M08 + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M08").WithArguments("virtual").WithLocation(25, 35), + // (25,35): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // virtual sealed static event D M08 + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M08").WithArguments("sealed", "7.3", "preview").WithLocation(25, 35), + // (28,37): error CS0112: A static member cannot be marked as 'virtual' + // virtual override static event D M09 + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M09").WithArguments("virtual").WithLocation(28, 37), + // (28,37): error CS0106: The modifier 'override' is not valid for this item + // virtual override static event D M09 + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M09").WithArguments("override").WithLocation(28, 37), + // (28,37): error CS8703: The modifier 'static' is not valid for this item in C# 7.3. Please use language version '8.0' or greater. + // virtual override static event D M09 + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M09").WithArguments("static", "7.3", "8.0").WithLocation(28, 37), + // (31,36): error CS0106: The modifier 'override' is not valid for this item + // sealed override static event D M10 + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M10").WithArguments("override").WithLocation(31, 36), + // (31,36): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // sealed override static event D M10 + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M10").WithArguments("sealed", "7.3", "preview").WithLocation(31, 36) + ); + + ValidateEventModifiers_01(compilation1); + } + + [Fact] + public void EventModifiers_06() + { + var source1 = +@"#pragma warning disable CS0067 // The event is never used +public interface I1 +{ + abstract static event D M01 { add {} remove {} } + + + virtual static event D M02 { add {} remove {} } + + + sealed static event D M03 { add {} remove {} } + + + override static event D M04 { add {} remove {} } + + + abstract virtual static event D M05 { add {} remove {} } + + + abstract sealed static event D M06 { add {} remove {} } + + + abstract override static event D M07 { add {} remove {} } + + + virtual sealed static event D M08 { add {} remove {} } + + + virtual override static event D M09 { add {} remove {} } + + + sealed override static event D M10 { add {} remove {} } +} + +public delegate void D(); +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular7_3, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,29): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract static event D M01 { add {} remove {} } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "7.3", "preview").WithLocation(4, 29), + // (4,33): error CS8712: 'I1.M01': abstract event cannot use event accessor syntax + // abstract static event D M01 { add {} remove {} } + Diagnostic(ErrorCode.ERR_AbstractEventHasAccessors, "{").WithArguments("I1.M01").WithLocation(4, 33), + // (7,28): error CS0112: A static member cannot be marked as 'virtual' + // virtual static event D M02 { add {} remove {} } + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M02").WithArguments("virtual").WithLocation(7, 28), + // (7,28): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + // virtual static event D M02 { add {} remove {} } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "M02").WithArguments("default interface implementation", "8.0").WithLocation(7, 28), + // (10,27): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // sealed static event D M03 { add {} remove {} } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M03").WithArguments("sealed", "7.3", "preview").WithLocation(10, 27), + // (13,29): error CS0106: The modifier 'override' is not valid for this item + // override static event D M04 { add {} remove {} } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M04").WithArguments("override").WithLocation(13, 29), + // (13,29): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + // override static event D M04 { add {} remove {} } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "M04").WithArguments("default interface implementation", "8.0").WithLocation(13, 29), + // (16,37): error CS0112: A static member cannot be marked as 'virtual' + // abstract virtual static event D M05 { add {} remove {} } + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M05").WithArguments("virtual").WithLocation(16, 37), + // (16,37): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract virtual static event D M05 { add {} remove {} } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M05").WithArguments("abstract", "7.3", "preview").WithLocation(16, 37), + // (16,41): error CS8712: 'I1.M05': abstract event cannot use event accessor syntax + // abstract virtual static event D M05 { add {} remove {} } + Diagnostic(ErrorCode.ERR_AbstractEventHasAccessors, "{").WithArguments("I1.M05").WithLocation(16, 41), + // (19,36): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static event D M06 { add {} remove {} } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M06").WithArguments("sealed").WithLocation(19, 36), + // (19,36): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract sealed static event D M06 { add {} remove {} } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M06").WithArguments("abstract", "7.3", "preview").WithLocation(19, 36), + // (19,40): error CS8712: 'I1.M06': abstract event cannot use event accessor syntax + // abstract sealed static event D M06 { add {} remove {} } + Diagnostic(ErrorCode.ERR_AbstractEventHasAccessors, "{").WithArguments("I1.M06").WithLocation(19, 40), + // (22,38): error CS0106: The modifier 'override' is not valid for this item + // abstract override static event D M07 { add {} remove {} } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M07").WithArguments("override").WithLocation(22, 38), + // (22,38): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract override static event D M07 { add {} remove {} } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M07").WithArguments("abstract", "7.3", "preview").WithLocation(22, 38), + // (22,42): error CS8712: 'I1.M07': abstract event cannot use event accessor syntax + // abstract override static event D M07 { add {} remove {} } + Diagnostic(ErrorCode.ERR_AbstractEventHasAccessors, "{").WithArguments("I1.M07").WithLocation(22, 42), + // (25,35): error CS0112: A static member cannot be marked as 'virtual' + // virtual sealed static event D M08 { add {} remove {} } + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M08").WithArguments("virtual").WithLocation(25, 35), + // (25,35): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // virtual sealed static event D M08 { add {} remove {} } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M08").WithArguments("sealed", "7.3", "preview").WithLocation(25, 35), + // (28,37): error CS0112: A static member cannot be marked as 'virtual' + // virtual override static event D M09 { add {} remove {} } + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M09").WithArguments("virtual").WithLocation(28, 37), + // (28,37): error CS0106: The modifier 'override' is not valid for this item + // virtual override static event D M09 { add {} remove {} } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M09").WithArguments("override").WithLocation(28, 37), + // (28,37): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + // virtual override static event D M09 { add {} remove {} } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "M09").WithArguments("default interface implementation", "8.0").WithLocation(28, 37), + // (31,36): error CS0106: The modifier 'override' is not valid for this item + // sealed override static event D M10 { add {} remove {} } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M10").WithArguments("override").WithLocation(31, 36), + // (31,36): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // sealed override static event D M10 { add {} remove {} } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M10").WithArguments("sealed", "7.3", "preview").WithLocation(31, 36) + ); + + ValidateEventModifiers_01(compilation1); + } + + [Fact] + public void OperatorModifiers_01() + { + var source1 = +@" +public interface I1 +{ + abstract static I1 operator+ (I1 x) + ; + + virtual static I1 operator- (I1 x) + ; + + sealed static I1 operator++ (I1 x) + ; + + override static I1 operator-- (I1 x) + ; + + abstract virtual static I1 operator! (I1 x) + ; + + abstract sealed static I1 operator~ (I1 x) + ; + + abstract override static I1 operator+ (I1 x, I1 y) + ; + + virtual sealed static I1 operator- (I1 x, I1 y) + ; + + virtual override static I1 operator* (I1 x, I1 y) + ; + + sealed override static I1 operator/ (I1 x, I1 y) + ; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,32): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static I1 operator+ (I1 x) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "+").WithArguments("abstract", "9.0", "preview").WithLocation(4, 32), + // (7,31): error CS0106: The modifier 'virtual' is not valid for this item + // virtual static I1 operator- (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "-").WithArguments("virtual").WithLocation(7, 31), + // (7,31): error CS0501: 'I1.operator -(I1)' must declare a body because it is not marked abstract, extern, or partial + // virtual static I1 operator- (I1 x) + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "-").WithArguments("I1.operator -(I1)").WithLocation(7, 31), + // (10,30): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // sealed static I1 operator++ (I1 x) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "++").WithArguments("sealed", "9.0", "preview").WithLocation(10, 30), + // (10,30): error CS0501: 'I1.operator ++(I1)' must declare a body because it is not marked abstract, extern, or partial + // sealed static I1 operator++ (I1 x) + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "++").WithArguments("I1.operator ++(I1)").WithLocation(10, 30), + // (13,32): error CS0106: The modifier 'override' is not valid for this item + // override static I1 operator-- (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "--").WithArguments("override").WithLocation(13, 32), + // (13,32): error CS0501: 'I1.operator --(I1)' must declare a body because it is not marked abstract, extern, or partial + // override static I1 operator-- (I1 x) + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "--").WithArguments("I1.operator --(I1)").WithLocation(13, 32), + // (16,40): error CS0106: The modifier 'virtual' is not valid for this item + // abstract virtual static I1 operator! (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "!").WithArguments("virtual").WithLocation(16, 40), + // (16,40): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract virtual static I1 operator! (I1 x) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "!").WithArguments("abstract", "9.0", "preview").WithLocation(16, 40), + // (19,39): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static I1 operator~ (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "~").WithArguments("sealed").WithLocation(19, 39), + // (19,39): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract sealed static I1 operator~ (I1 x) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "~").WithArguments("abstract", "9.0", "preview").WithLocation(19, 39), + // (22,41): error CS0106: The modifier 'override' is not valid for this item + // abstract override static I1 operator+ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("override").WithLocation(22, 41), + // (22,41): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract override static I1 operator+ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "+").WithArguments("abstract", "9.0", "preview").WithLocation(22, 41), + // (25,38): error CS0106: The modifier 'virtual' is not valid for this item + // virtual sealed static I1 operator- (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "-").WithArguments("virtual").WithLocation(25, 38), + // (25,38): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // virtual sealed static I1 operator- (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "-").WithArguments("sealed", "9.0", "preview").WithLocation(25, 38), + // (25,38): error CS0501: 'I1.operator -(I1, I1)' must declare a body because it is not marked abstract, extern, or partial + // virtual sealed static I1 operator- (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "-").WithArguments("I1.operator -(I1, I1)").WithLocation(25, 38), + // (28,40): error CS0106: The modifier 'virtual' is not valid for this item + // virtual override static I1 operator* (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "*").WithArguments("virtual").WithLocation(28, 40), + // (28,40): error CS0106: The modifier 'override' is not valid for this item + // virtual override static I1 operator* (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "*").WithArguments("override").WithLocation(28, 40), + // (28,40): error CS0501: 'I1.operator *(I1, I1)' must declare a body because it is not marked abstract, extern, or partial + // virtual override static I1 operator* (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "*").WithArguments("I1.operator *(I1, I1)").WithLocation(28, 40), + // (31,39): error CS0106: The modifier 'override' is not valid for this item + // sealed override static I1 operator/ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "/").WithArguments("override").WithLocation(31, 39), + // (31,39): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // sealed override static I1 operator/ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "/").WithArguments("sealed", "9.0", "preview").WithLocation(31, 39), + // (31,39): error CS0501: 'I1.operator /(I1, I1)' must declare a body because it is not marked abstract, extern, or partial + // sealed override static I1 operator/ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "/").WithArguments("I1.operator /(I1, I1)").WithLocation(31, 39) + ); + + ValidateOperatorModifiers_01(compilation1); + } + + private static void ValidateOperatorModifiers_01(CSharpCompilation compilation1) + { + var i1 = compilation1.GetTypeByMetadataName("I1"); + var m01 = i1.GetMember("op_UnaryPlus"); + + Assert.True(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.True(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + var m02 = i1.GetMember("op_UnaryNegation"); + + Assert.False(m02.IsAbstract); + Assert.False(m02.IsVirtual); + Assert.False(m02.IsMetadataVirtual()); + Assert.False(m02.IsSealed); + Assert.True(m02.IsStatic); + Assert.False(m02.IsExtern); + Assert.False(m02.IsAsync); + Assert.False(m02.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m02)); + + var m03 = i1.GetMember("op_Increment"); + + Assert.False(m03.IsAbstract); + Assert.False(m03.IsVirtual); + Assert.False(m03.IsMetadataVirtual()); + Assert.False(m03.IsSealed); + Assert.True(m03.IsStatic); + Assert.False(m03.IsExtern); + Assert.False(m03.IsAsync); + Assert.False(m03.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m03)); + + var m04 = i1.GetMember("op_Decrement"); + + Assert.False(m04.IsAbstract); + Assert.False(m04.IsVirtual); + Assert.False(m04.IsMetadataVirtual()); + Assert.False(m04.IsSealed); + Assert.True(m04.IsStatic); + Assert.False(m04.IsExtern); + Assert.False(m04.IsAsync); + Assert.False(m04.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m04)); + + var m05 = i1.GetMember("op_LogicalNot"); + + Assert.True(m05.IsAbstract); + Assert.False(m05.IsVirtual); + Assert.True(m05.IsMetadataVirtual()); + Assert.False(m05.IsSealed); + Assert.True(m05.IsStatic); + Assert.False(m05.IsExtern); + Assert.False(m05.IsAsync); + Assert.False(m05.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m05)); + + var m06 = i1.GetMember("op_OnesComplement"); + + Assert.True(m06.IsAbstract); + Assert.False(m06.IsVirtual); + Assert.True(m06.IsMetadataVirtual()); + Assert.False(m06.IsSealed); + Assert.True(m06.IsStatic); + Assert.False(m06.IsExtern); + Assert.False(m06.IsAsync); + Assert.False(m06.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m06)); + + var m07 = i1.GetMember("op_Addition"); + + Assert.True(m07.IsAbstract); + Assert.False(m07.IsVirtual); + Assert.True(m07.IsMetadataVirtual()); + Assert.False(m07.IsSealed); + Assert.True(m07.IsStatic); + Assert.False(m07.IsExtern); + Assert.False(m07.IsAsync); + Assert.False(m07.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m07)); + + var m08 = i1.GetMember("op_Subtraction"); + + Assert.False(m08.IsAbstract); + Assert.False(m08.IsVirtual); + Assert.False(m08.IsMetadataVirtual()); + Assert.False(m08.IsSealed); + Assert.True(m08.IsStatic); + Assert.False(m08.IsExtern); + Assert.False(m08.IsAsync); + Assert.False(m08.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m08)); + + var m09 = i1.GetMember("op_Multiply"); + + Assert.False(m09.IsAbstract); + Assert.False(m09.IsVirtual); + Assert.False(m09.IsMetadataVirtual()); + Assert.False(m09.IsSealed); + Assert.True(m09.IsStatic); + Assert.False(m09.IsExtern); + Assert.False(m09.IsAsync); + Assert.False(m09.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m09)); + + var m10 = i1.GetMember("op_Division"); + + Assert.False(m10.IsAbstract); + Assert.False(m10.IsVirtual); + Assert.False(m10.IsMetadataVirtual()); + Assert.False(m10.IsSealed); + Assert.True(m10.IsStatic); + Assert.False(m10.IsExtern); + Assert.False(m10.IsAsync); + Assert.False(m10.IsOverride); + Assert.Null(i1.FindImplementationForInterfaceMember(m10)); + } + + [Fact] + public void OperatorModifiers_02() + { + var source1 = +@" +public interface I1 +{ + abstract static I1 operator+ (I1 x) + {throw null;} + + virtual static I1 operator- (I1 x) + {throw null;} + + sealed static I1 operator++ (I1 x) + {throw null;} + + override static I1 operator-- (I1 x) + {throw null;} + + abstract virtual static I1 operator! (I1 x) + {throw null;} + + abstract sealed static I1 operator~ (I1 x) + {throw null;} + + abstract override static I1 operator+ (I1 x, I1 y) + {throw null;} + + virtual sealed static I1 operator- (I1 x, I1 y) + {throw null;} + + virtual override static I1 operator* (I1 x, I1 y) + {throw null;} + + sealed override static I1 operator/ (I1 x, I1 y) + {throw null;} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,32): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static I1 operator+ (I1 x) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "+").WithArguments("abstract", "9.0", "preview").WithLocation(4, 32), + // (4,32): error CS0500: 'I1.operator +(I1)' cannot declare a body because it is marked abstract + // abstract static I1 operator+ (I1 x) + Diagnostic(ErrorCode.ERR_AbstractHasBody, "+").WithArguments("I1.operator +(I1)").WithLocation(4, 32), + // (7,31): error CS0106: The modifier 'virtual' is not valid for this item + // virtual static I1 operator- (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "-").WithArguments("virtual").WithLocation(7, 31), + // (10,30): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // sealed static I1 operator++ (I1 x) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "++").WithArguments("sealed", "9.0", "preview").WithLocation(10, 30), + // (13,32): error CS0106: The modifier 'override' is not valid for this item + // override static I1 operator-- (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "--").WithArguments("override").WithLocation(13, 32), + // (16,40): error CS0106: The modifier 'virtual' is not valid for this item + // abstract virtual static I1 operator! (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "!").WithArguments("virtual").WithLocation(16, 40), + // (16,40): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract virtual static I1 operator! (I1 x) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "!").WithArguments("abstract", "9.0", "preview").WithLocation(16, 40), + // (16,40): error CS0500: 'I1.operator !(I1)' cannot declare a body because it is marked abstract + // abstract virtual static I1 operator! (I1 x) + Diagnostic(ErrorCode.ERR_AbstractHasBody, "!").WithArguments("I1.operator !(I1)").WithLocation(16, 40), + // (19,39): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static I1 operator~ (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "~").WithArguments("sealed").WithLocation(19, 39), + // (19,39): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract sealed static I1 operator~ (I1 x) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "~").WithArguments("abstract", "9.0", "preview").WithLocation(19, 39), + // (19,39): error CS0500: 'I1.operator ~(I1)' cannot declare a body because it is marked abstract + // abstract sealed static I1 operator~ (I1 x) + Diagnostic(ErrorCode.ERR_AbstractHasBody, "~").WithArguments("I1.operator ~(I1)").WithLocation(19, 39), + // (22,41): error CS0106: The modifier 'override' is not valid for this item + // abstract override static I1 operator+ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("override").WithLocation(22, 41), + // (22,41): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract override static I1 operator+ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "+").WithArguments("abstract", "9.0", "preview").WithLocation(22, 41), + // (22,41): error CS0500: 'I1.operator +(I1, I1)' cannot declare a body because it is marked abstract + // abstract override static I1 operator+ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_AbstractHasBody, "+").WithArguments("I1.operator +(I1, I1)").WithLocation(22, 41), + // (25,38): error CS0106: The modifier 'virtual' is not valid for this item + // virtual sealed static I1 operator- (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "-").WithArguments("virtual").WithLocation(25, 38), + // (25,38): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // virtual sealed static I1 operator- (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "-").WithArguments("sealed", "9.0", "preview").WithLocation(25, 38), + // (28,40): error CS0106: The modifier 'virtual' is not valid for this item + // virtual override static I1 operator* (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "*").WithArguments("virtual").WithLocation(28, 40), + // (28,40): error CS0106: The modifier 'override' is not valid for this item + // virtual override static I1 operator* (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "*").WithArguments("override").WithLocation(28, 40), + // (31,39): error CS0106: The modifier 'override' is not valid for this item + // sealed override static I1 operator/ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "/").WithArguments("override").WithLocation(31, 39), + // (31,39): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // sealed override static I1 operator/ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "/").WithArguments("sealed", "9.0", "preview").WithLocation(31, 39) + ); + + ValidateOperatorModifiers_01(compilation1); + } + + [Fact] + public void OperatorModifiers_03() + { + var source1 = +@" +public interface I1 +{ + abstract static I1 operator+ (I1 x) + ; + + virtual static I1 operator- (I1 x) + ; + + sealed static I1 operator++ (I1 x) + ; + + override static I1 operator-- (I1 x) + ; + + abstract virtual static I1 operator! (I1 x) + ; + + abstract sealed static I1 operator~ (I1 x) + ; + + abstract override static I1 operator+ (I1 x, I1 y) + ; + + virtual sealed static I1 operator- (I1 x, I1 y) + ; + + virtual override static I1 operator* (I1 x, I1 y) + ; + + sealed override static I1 operator/ (I1 x, I1 y) + ; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (7,31): error CS0106: The modifier 'virtual' is not valid for this item + // virtual static I1 operator- (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "-").WithArguments("virtual").WithLocation(7, 31), + // (7,31): error CS0501: 'I1.operator -(I1)' must declare a body because it is not marked abstract, extern, or partial + // virtual static I1 operator- (I1 x) + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "-").WithArguments("I1.operator -(I1)").WithLocation(7, 31), + // (10,30): error CS0501: 'I1.operator ++(I1)' must declare a body because it is not marked abstract, extern, or partial + // sealed static I1 operator++ (I1 x) + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "++").WithArguments("I1.operator ++(I1)").WithLocation(10, 30), + // (13,32): error CS0106: The modifier 'override' is not valid for this item + // override static I1 operator-- (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "--").WithArguments("override").WithLocation(13, 32), + // (13,32): error CS0501: 'I1.operator --(I1)' must declare a body because it is not marked abstract, extern, or partial + // override static I1 operator-- (I1 x) + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "--").WithArguments("I1.operator --(I1)").WithLocation(13, 32), + // (16,40): error CS0106: The modifier 'virtual' is not valid for this item + // abstract virtual static I1 operator! (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "!").WithArguments("virtual").WithLocation(16, 40), + // (19,39): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static I1 operator~ (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "~").WithArguments("sealed").WithLocation(19, 39), + // (22,41): error CS0106: The modifier 'override' is not valid for this item + // abstract override static I1 operator+ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("override").WithLocation(22, 41), + // (25,38): error CS0106: The modifier 'virtual' is not valid for this item + // virtual sealed static I1 operator- (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "-").WithArguments("virtual").WithLocation(25, 38), + // (25,38): error CS0501: 'I1.operator -(I1, I1)' must declare a body because it is not marked abstract, extern, or partial + // virtual sealed static I1 operator- (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "-").WithArguments("I1.operator -(I1, I1)").WithLocation(25, 38), + // (28,40): error CS0106: The modifier 'virtual' is not valid for this item + // virtual override static I1 operator* (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "*").WithArguments("virtual").WithLocation(28, 40), + // (28,40): error CS0106: The modifier 'override' is not valid for this item + // virtual override static I1 operator* (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "*").WithArguments("override").WithLocation(28, 40), + // (28,40): error CS0501: 'I1.operator *(I1, I1)' must declare a body because it is not marked abstract, extern, or partial + // virtual override static I1 operator* (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "*").WithArguments("I1.operator *(I1, I1)").WithLocation(28, 40), + // (31,39): error CS0106: The modifier 'override' is not valid for this item + // sealed override static I1 operator/ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "/").WithArguments("override").WithLocation(31, 39), + // (31,39): error CS0501: 'I1.operator /(I1, I1)' must declare a body because it is not marked abstract, extern, or partial + // sealed override static I1 operator/ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "/").WithArguments("I1.operator /(I1, I1)").WithLocation(31, 39) + ); + + ValidateOperatorModifiers_01(compilation1); + } + + [Fact] + public void OperatorModifiers_04() + { + var source1 = +@" +public interface I1 +{ + abstract static I1 operator+ (I1 x) + {throw null;} + + virtual static I1 operator- (I1 x) + {throw null;} + + sealed static I1 operator++ (I1 x) + {throw null;} + + override static I1 operator-- (I1 x) + {throw null;} + + abstract virtual static I1 operator! (I1 x) + {throw null;} + + abstract sealed static I1 operator~ (I1 x) + {throw null;} + + abstract override static I1 operator+ (I1 x, I1 y) + {throw null;} + + virtual sealed static I1 operator- (I1 x, I1 y) + {throw null;} + + virtual override static I1 operator* (I1 x, I1 y) + {throw null;} + + sealed override static I1 operator/ (I1 x, I1 y) + {throw null;} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,32): error CS0500: 'I1.operator +(I1)' cannot declare a body because it is marked abstract + // abstract static I1 operator+ (I1 x) + Diagnostic(ErrorCode.ERR_AbstractHasBody, "+").WithArguments("I1.operator +(I1)").WithLocation(4, 32), + // (7,31): error CS0106: The modifier 'virtual' is not valid for this item + // virtual static I1 operator- (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "-").WithArguments("virtual").WithLocation(7, 31), + // (13,32): error CS0106: The modifier 'override' is not valid for this item + // override static I1 operator-- (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "--").WithArguments("override").WithLocation(13, 32), + // (16,40): error CS0106: The modifier 'virtual' is not valid for this item + // abstract virtual static I1 operator! (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "!").WithArguments("virtual").WithLocation(16, 40), + // (16,40): error CS0500: 'I1.operator !(I1)' cannot declare a body because it is marked abstract + // abstract virtual static I1 operator! (I1 x) + Diagnostic(ErrorCode.ERR_AbstractHasBody, "!").WithArguments("I1.operator !(I1)").WithLocation(16, 40), + // (19,39): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static I1 operator~ (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "~").WithArguments("sealed").WithLocation(19, 39), + // (19,39): error CS0500: 'I1.operator ~(I1)' cannot declare a body because it is marked abstract + // abstract sealed static I1 operator~ (I1 x) + Diagnostic(ErrorCode.ERR_AbstractHasBody, "~").WithArguments("I1.operator ~(I1)").WithLocation(19, 39), + // (22,41): error CS0106: The modifier 'override' is not valid for this item + // abstract override static I1 operator+ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("override").WithLocation(22, 41), + // (22,41): error CS0500: 'I1.operator +(I1, I1)' cannot declare a body because it is marked abstract + // abstract override static I1 operator+ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_AbstractHasBody, "+").WithArguments("I1.operator +(I1, I1)").WithLocation(22, 41), + // (25,38): error CS0106: The modifier 'virtual' is not valid for this item + // virtual sealed static I1 operator- (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "-").WithArguments("virtual").WithLocation(25, 38), + // (28,40): error CS0106: The modifier 'virtual' is not valid for this item + // virtual override static I1 operator* (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "*").WithArguments("virtual").WithLocation(28, 40), + // (28,40): error CS0106: The modifier 'override' is not valid for this item + // virtual override static I1 operator* (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "*").WithArguments("override").WithLocation(28, 40), + // (31,39): error CS0106: The modifier 'override' is not valid for this item + // sealed override static I1 operator/ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "/").WithArguments("override").WithLocation(31, 39) + ); + + ValidateOperatorModifiers_01(compilation1); + } + + [Fact] + public void OperatorModifiers_05() + { + var source1 = +@" +public interface I1 +{ + abstract static I1 operator+ (I1 x) + ; + + virtual static I1 operator- (I1 x) + ; + + sealed static I1 operator++ (I1 x) + ; + + override static I1 operator-- (I1 x) + ; + + abstract virtual static I1 operator! (I1 x) + ; + + abstract sealed static I1 operator~ (I1 x) + ; + + abstract override static I1 operator+ (I1 x, I1 y) + ; + + virtual sealed static I1 operator- (I1 x, I1 y) + ; + + virtual override static I1 operator* (I1 x, I1 y) + ; + + sealed override static I1 operator/ (I1 x, I1 y) + ; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular7_3, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,32): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract static I1 operator+ (I1 x) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "+").WithArguments("abstract", "7.3", "preview").WithLocation(4, 32), + // (7,31): error CS0106: The modifier 'virtual' is not valid for this item + // virtual static I1 operator- (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "-").WithArguments("virtual").WithLocation(7, 31), + // (7,31): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + // virtual static I1 operator- (I1 x) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "-").WithArguments("default interface implementation", "8.0").WithLocation(7, 31), + // (7,31): error CS0501: 'I1.operator -(I1)' must declare a body because it is not marked abstract, extern, or partial + // virtual static I1 operator- (I1 x) + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "-").WithArguments("I1.operator -(I1)").WithLocation(7, 31), + // (10,30): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // sealed static I1 operator++ (I1 x) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "++").WithArguments("sealed", "7.3", "preview").WithLocation(10, 30), + // (10,30): error CS0501: 'I1.operator ++(I1)' must declare a body because it is not marked abstract, extern, or partial + // sealed static I1 operator++ (I1 x) + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "++").WithArguments("I1.operator ++(I1)").WithLocation(10, 30), + // (13,32): error CS0106: The modifier 'override' is not valid for this item + // override static I1 operator-- (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "--").WithArguments("override").WithLocation(13, 32), + // (13,32): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + // override static I1 operator-- (I1 x) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "--").WithArguments("default interface implementation", "8.0").WithLocation(13, 32), + // (13,32): error CS0501: 'I1.operator --(I1)' must declare a body because it is not marked abstract, extern, or partial + // override static I1 operator-- (I1 x) + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "--").WithArguments("I1.operator --(I1)").WithLocation(13, 32), + // (16,40): error CS0106: The modifier 'virtual' is not valid for this item + // abstract virtual static I1 operator! (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "!").WithArguments("virtual").WithLocation(16, 40), + // (16,40): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract virtual static I1 operator! (I1 x) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "!").WithArguments("abstract", "7.3", "preview").WithLocation(16, 40), + // (19,39): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static I1 operator~ (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "~").WithArguments("sealed").WithLocation(19, 39), + // (19,39): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract sealed static I1 operator~ (I1 x) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "~").WithArguments("abstract", "7.3", "preview").WithLocation(19, 39), + // (22,41): error CS0106: The modifier 'override' is not valid for this item + // abstract override static I1 operator+ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("override").WithLocation(22, 41), + // (22,41): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract override static I1 operator+ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "+").WithArguments("abstract", "7.3", "preview").WithLocation(22, 41), + // (25,38): error CS0106: The modifier 'virtual' is not valid for this item + // virtual sealed static I1 operator- (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "-").WithArguments("virtual").WithLocation(25, 38), + // (25,38): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // virtual sealed static I1 operator- (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "-").WithArguments("sealed", "7.3", "preview").WithLocation(25, 38), + // (25,38): error CS0501: 'I1.operator -(I1, I1)' must declare a body because it is not marked abstract, extern, or partial + // virtual sealed static I1 operator- (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "-").WithArguments("I1.operator -(I1, I1)").WithLocation(25, 38), + // (28,40): error CS0106: The modifier 'virtual' is not valid for this item + // virtual override static I1 operator* (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "*").WithArguments("virtual").WithLocation(28, 40), + // (28,40): error CS0106: The modifier 'override' is not valid for this item + // virtual override static I1 operator* (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "*").WithArguments("override").WithLocation(28, 40), + // (28,40): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + // virtual override static I1 operator* (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "*").WithArguments("default interface implementation", "8.0").WithLocation(28, 40), + // (28,40): error CS0501: 'I1.operator *(I1, I1)' must declare a body because it is not marked abstract, extern, or partial + // virtual override static I1 operator* (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "*").WithArguments("I1.operator *(I1, I1)").WithLocation(28, 40), + // (31,39): error CS0106: The modifier 'override' is not valid for this item + // sealed override static I1 operator/ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "/").WithArguments("override").WithLocation(31, 39), + // (31,39): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // sealed override static I1 operator/ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "/").WithArguments("sealed", "7.3", "preview").WithLocation(31, 39), + // (31,39): error CS0501: 'I1.operator /(I1, I1)' must declare a body because it is not marked abstract, extern, or partial + // sealed override static I1 operator/ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "/").WithArguments("I1.operator /(I1, I1)").WithLocation(31, 39) + ); + + ValidateOperatorModifiers_01(compilation1); + } + + [Fact] + public void OperatorModifiers_06() + { + var source1 = +@" +public interface I1 +{ + abstract static I1 operator+ (I1 x) + {throw null;} + + virtual static I1 operator- (I1 x) + {throw null;} + + sealed static I1 operator++ (I1 x) + {throw null;} + + override static I1 operator-- (I1 x) + {throw null;} + + abstract virtual static I1 operator! (I1 x) + {throw null;} + + abstract sealed static I1 operator~ (I1 x) + {throw null;} + + abstract override static I1 operator+ (I1 x, I1 y) + {throw null;} + + virtual sealed static I1 operator- (I1 x, I1 y) + {throw null;} + + virtual override static I1 operator* (I1 x, I1 y) + {throw null;} + + sealed override static I1 operator/ (I1 x, I1 y) + {throw null;} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular7_3, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,32): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract static I1 operator+ (I1 x) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "+").WithArguments("abstract", "7.3", "preview").WithLocation(4, 32), + // (4,32): error CS0500: 'I1.operator +(I1)' cannot declare a body because it is marked abstract + // abstract static I1 operator+ (I1 x) + Diagnostic(ErrorCode.ERR_AbstractHasBody, "+").WithArguments("I1.operator +(I1)").WithLocation(4, 32), + // (7,31): error CS0106: The modifier 'virtual' is not valid for this item + // virtual static I1 operator- (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "-").WithArguments("virtual").WithLocation(7, 31), + // (7,31): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + // virtual static I1 operator- (I1 x) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "-").WithArguments("default interface implementation", "8.0").WithLocation(7, 31), + // (10,30): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // sealed static I1 operator++ (I1 x) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "++").WithArguments("sealed", "7.3", "preview").WithLocation(10, 30), + // (13,32): error CS0106: The modifier 'override' is not valid for this item + // override static I1 operator-- (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "--").WithArguments("override").WithLocation(13, 32), + // (13,32): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + // override static I1 operator-- (I1 x) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "--").WithArguments("default interface implementation", "8.0").WithLocation(13, 32), + // (16,40): error CS0106: The modifier 'virtual' is not valid for this item + // abstract virtual static I1 operator! (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "!").WithArguments("virtual").WithLocation(16, 40), + // (16,40): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract virtual static I1 operator! (I1 x) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "!").WithArguments("abstract", "7.3", "preview").WithLocation(16, 40), + // (16,40): error CS0500: 'I1.operator !(I1)' cannot declare a body because it is marked abstract + // abstract virtual static I1 operator! (I1 x) + Diagnostic(ErrorCode.ERR_AbstractHasBody, "!").WithArguments("I1.operator !(I1)").WithLocation(16, 40), + // (19,39): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static I1 operator~ (I1 x) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "~").WithArguments("sealed").WithLocation(19, 39), + // (19,39): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract sealed static I1 operator~ (I1 x) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "~").WithArguments("abstract", "7.3", "preview").WithLocation(19, 39), + // (19,39): error CS0500: 'I1.operator ~(I1)' cannot declare a body because it is marked abstract + // abstract sealed static I1 operator~ (I1 x) + Diagnostic(ErrorCode.ERR_AbstractHasBody, "~").WithArguments("I1.operator ~(I1)").WithLocation(19, 39), + // (22,41): error CS0106: The modifier 'override' is not valid for this item + // abstract override static I1 operator+ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("override").WithLocation(22, 41), + // (22,41): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract override static I1 operator+ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "+").WithArguments("abstract", "7.3", "preview").WithLocation(22, 41), + // (22,41): error CS0500: 'I1.operator +(I1, I1)' cannot declare a body because it is marked abstract + // abstract override static I1 operator+ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_AbstractHasBody, "+").WithArguments("I1.operator +(I1, I1)").WithLocation(22, 41), + // (25,38): error CS0106: The modifier 'virtual' is not valid for this item + // virtual sealed static I1 operator- (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "-").WithArguments("virtual").WithLocation(25, 38), + // (25,38): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // virtual sealed static I1 operator- (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "-").WithArguments("sealed", "7.3", "preview").WithLocation(25, 38), + // (28,40): error CS0106: The modifier 'virtual' is not valid for this item + // virtual override static I1 operator* (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "*").WithArguments("virtual").WithLocation(28, 40), + // (28,40): error CS0106: The modifier 'override' is not valid for this item + // virtual override static I1 operator* (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "*").WithArguments("override").WithLocation(28, 40), + // (28,40): error CS8370: Feature 'default interface implementation' is not available in C# 7.3. Please use language version 8.0 or greater. + // virtual override static I1 operator* (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "*").WithArguments("default interface implementation", "8.0").WithLocation(28, 40), + // (31,39): error CS0106: The modifier 'override' is not valid for this item + // sealed override static I1 operator/ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_BadMemberFlag, "/").WithArguments("override").WithLocation(31, 39), + // (31,39): error CS8703: The modifier 'sealed' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // sealed override static I1 operator/ (I1 x, I1 y) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "/").WithArguments("sealed", "7.3", "preview").WithLocation(31, 39) + ); + + ValidateOperatorModifiers_01(compilation1); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void OperatorModifiers_07(bool use7_3) + { + var source1 = +@" +public interface I1 +{ + abstract static bool operator== (I1 x, I1 y); + + abstract static bool operator!= (I1 x, I1 y) {return false;} +} + +public interface I2 +{ + sealed static bool operator== (I2 x, I2 y) {return false;} + + sealed static bool operator!= (I2 x, I2 y) {return false;} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: use7_3 ? TestOptions.Regular7_3 : TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,34): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // abstract static bool operator== (I1 x, I1 y); + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "==").WithLocation(4, 34), + // (6,34): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // abstract static bool operator!= (I1 x, I1 y) {return false;} + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "!=").WithLocation(6, 34), + // (11,32): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // sealed static bool operator== (I2 x, I2 y) {return false;} + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "==").WithLocation(11, 32), + // (13,32): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // sealed static bool operator!= (I2 x, I2 y) {return false;} + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "!=").WithLocation(13, 32) + ); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void OperatorModifiers_08(bool use7_3) + { + var source1 = +@" +public interface I1 +{ + abstract static implicit operator int(I1 x); + + abstract static explicit operator bool(I1 x) {return false;} +} + +public interface I2 +{ + sealed static implicit operator int(I2 x) {return 0;} + + sealed static explicit operator bool(I2 x) {return false;} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: use7_3 ? TestOptions.Regular7_3 : TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,39): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // abstract static implicit operator int(I1 x); + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "int").WithLocation(4, 39), + // (6,39): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // abstract static explicit operator bool(I1 x) {return false;} + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "bool").WithLocation(6, 39), + // (11,37): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // sealed static implicit operator int(I2 x) {return 0;} + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "int").WithLocation(11, 37), + // (13,37): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // sealed static explicit operator bool(I2 x) {return false;} + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "bool").WithLocation(13, 37) + ); + } + + [Fact] + public void FieldModifiers_01() + { + var source1 = +@" +public interface I1 +{ + abstract static int F1; + sealed static int F2; + abstract int F3; + sealed int F4; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,25): error CS0681: The modifier 'abstract' is not valid on fields. Try using a property instead. + // abstract static int F1; + Diagnostic(ErrorCode.ERR_AbstractField, "F1").WithLocation(4, 25), + // (5,23): error CS0106: The modifier 'sealed' is not valid for this item + // sealed static int F2; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "F2").WithArguments("sealed").WithLocation(5, 23), + // (6,18): error CS0681: The modifier 'abstract' is not valid on fields. Try using a property instead. + // abstract int F3; + Diagnostic(ErrorCode.ERR_AbstractField, "F3").WithLocation(6, 18), + // (6,18): error CS0525: Interfaces cannot contain instance fields + // abstract int F3; + Diagnostic(ErrorCode.ERR_InterfacesCantContainFields, "F3").WithLocation(6, 18), + // (7,16): error CS0106: The modifier 'sealed' is not valid for this item + // sealed int F4; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "F4").WithArguments("sealed").WithLocation(7, 16), + // (7,16): error CS0525: Interfaces cannot contain instance fields + // sealed int F4; + Diagnostic(ErrorCode.ERR_InterfacesCantContainFields, "F4").WithLocation(7, 16) + ); + } + + [Fact] + public void ExternAbstractStatic_01() + { + var source1 = +@" +interface I1 +{ + extern abstract static void M01(); + extern abstract static bool P01 { get; } + extern abstract static event System.Action E01; + extern abstract static I1 operator+ (I1 x); +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,33): error CS0180: 'I1.M01()' cannot be both extern and abstract + // extern abstract static void M01(); + Diagnostic(ErrorCode.ERR_AbstractAndExtern, "M01").WithArguments("I1.M01()").WithLocation(4, 33), + // (5,33): error CS0180: 'I1.P01' cannot be both extern and abstract + // extern abstract static bool P01 { get; } + Diagnostic(ErrorCode.ERR_AbstractAndExtern, "P01").WithArguments("I1.P01").WithLocation(5, 33), + // (6,48): error CS0180: 'I1.E01' cannot be both extern and abstract + // extern abstract static event System.Action E01; + Diagnostic(ErrorCode.ERR_AbstractAndExtern, "E01").WithArguments("I1.E01").WithLocation(6, 48), + // (7,39): error CS0180: 'I1.operator +(I1)' cannot be both extern and abstract + // extern abstract static I1 operator+ (I1 x); + Diagnostic(ErrorCode.ERR_AbstractAndExtern, "+").WithArguments("I1.operator +(I1)").WithLocation(7, 39) + ); + } + + [Fact] + public void ExternAbstractStatic_02() + { + var source1 = +@" +interface I1 +{ + extern abstract static void M01() {} + extern abstract static bool P01 { get => false; } + extern abstract static event System.Action E01 { add {} remove {} } + extern abstract static I1 operator+ (I1 x) => null; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,33): error CS0180: 'I1.M01()' cannot be both extern and abstract + // extern abstract static void M01() {} + Diagnostic(ErrorCode.ERR_AbstractAndExtern, "M01").WithArguments("I1.M01()").WithLocation(4, 33), + // (5,33): error CS0180: 'I1.P01' cannot be both extern and abstract + // extern abstract static bool P01 { get => false; } + Diagnostic(ErrorCode.ERR_AbstractAndExtern, "P01").WithArguments("I1.P01").WithLocation(5, 33), + // (6,48): error CS0180: 'I1.E01' cannot be both extern and abstract + // extern abstract static event System.Action E01 { add {} remove {} } + Diagnostic(ErrorCode.ERR_AbstractAndExtern, "E01").WithArguments("I1.E01").WithLocation(6, 48), + // (6,52): error CS8712: 'I1.E01': abstract event cannot use event accessor syntax + // extern abstract static event System.Action E01 { add {} remove {} } + Diagnostic(ErrorCode.ERR_AbstractEventHasAccessors, "{").WithArguments("I1.E01").WithLocation(6, 52), + // (7,39): error CS0180: 'I1.operator +(I1)' cannot be both extern and abstract + // extern abstract static I1 operator+ (I1 x) => null; + Diagnostic(ErrorCode.ERR_AbstractAndExtern, "+").WithArguments("I1.operator +(I1)").WithLocation(7, 39) + ); + } + + [Fact] + public void ExternSealedStatic_01() + { + var source1 = +@" +#pragma warning disable CS0626 // Method, operator, or accessor is marked external and has no attributes on it. Consider adding a DllImport attribute to specify the external implementation. + +interface I1 +{ + extern sealed static void M01(); + extern sealed static bool P01 { get; } + extern sealed static event System.Action E01; + extern sealed static I1 operator+ (I1 x); +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + } + + [Fact] + public void AbstractStaticInClass_01() + { + var source1 = +@" +abstract class C1 +{ + public abstract static void M01(); + public abstract static bool P01 { get; } + public abstract static event System.Action E01; + public abstract static C1 operator+ (C1 x); +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,33): error CS0112: A static member cannot be marked as 'abstract' + // public abstract static void M01(); + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M01").WithArguments("abstract").WithLocation(4, 33), + // (5,33): error CS0112: A static member cannot be marked as 'abstract' + // public abstract static bool P01 { get; } + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "P01").WithArguments("abstract").WithLocation(5, 33), + // (6,48): error CS0112: A static member cannot be marked as 'abstract' + // public abstract static event System.Action E01; + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "E01").WithArguments("abstract").WithLocation(6, 48), + // (7,39): error CS0106: The modifier 'abstract' is not valid for this item + // public abstract static C1 operator+ (C1 x); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("abstract").WithLocation(7, 39), + // (7,39): error CS0501: 'C1.operator +(C1)' must declare a body because it is not marked abstract, extern, or partial + // public abstract static C1 operator+ (C1 x); + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "+").WithArguments("C1.operator +(C1)").WithLocation(7, 39) + ); + } + + [Fact] + public void SealedStaticInClass_01() + { + var source1 = +@" +class C1 +{ + sealed static void M01() {} + sealed static bool P01 { get => false; } + sealed static event System.Action E01 { add {} remove {} } + public sealed static C1 operator+ (C1 x) => null; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,24): error CS0238: 'C1.M01()' cannot be sealed because it is not an override + // sealed static void M01() {} + Diagnostic(ErrorCode.ERR_SealedNonOverride, "M01").WithArguments("C1.M01()").WithLocation(4, 24), + // (5,24): error CS0238: 'C1.P01' cannot be sealed because it is not an override + // sealed static bool P01 { get => false; } + Diagnostic(ErrorCode.ERR_SealedNonOverride, "P01").WithArguments("C1.P01").WithLocation(5, 24), + // (6,39): error CS0238: 'C1.E01' cannot be sealed because it is not an override + // sealed static event System.Action E01 { add {} remove {} } + Diagnostic(ErrorCode.ERR_SealedNonOverride, "E01").WithArguments("C1.E01").WithLocation(6, 39), + // (7,37): error CS0106: The modifier 'sealed' is not valid for this item + // public sealed static C1 operator+ (C1 x) => null; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("sealed").WithLocation(7, 37) + ); + } + + [Fact] + public void AbstractStaticInStruct_01() + { + var source1 = +@" +struct C1 +{ + public abstract static void M01(); + public abstract static bool P01 { get; } + public abstract static event System.Action E01; + public abstract static C1 operator+ (C1 x); +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,33): error CS0112: A static member cannot be marked as 'abstract' + // public abstract static void M01(); + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "M01").WithArguments("abstract").WithLocation(4, 33), + // (5,33): error CS0112: A static member cannot be marked as 'abstract' + // public abstract static bool P01 { get; } + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "P01").WithArguments("abstract").WithLocation(5, 33), + // (6,48): error CS0112: A static member cannot be marked as 'abstract' + // public abstract static event System.Action E01; + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "E01").WithArguments("abstract").WithLocation(6, 48), + // (7,39): error CS0106: The modifier 'abstract' is not valid for this item + // public abstract static C1 operator+ (C1 x); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("abstract").WithLocation(7, 39), + // (7,39): error CS0501: 'C1.operator +(C1)' must declare a body because it is not marked abstract, extern, or partial + // public abstract static C1 operator+ (C1 x); + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "+").WithArguments("C1.operator +(C1)").WithLocation(7, 39) + ); + } + + [Fact] + public void SealedStaticInstruct_01() + { + var source1 = +@" +class C1 +{ + sealed static void M01() {} + sealed static bool P01 { get => false; } + sealed static event System.Action E01 { add {} remove {} } + public sealed static C1 operator+ (C1 x) => null; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,24): error CS0238: 'C1.M01()' cannot be sealed because it is not an override + // sealed static void M01() {} + Diagnostic(ErrorCode.ERR_SealedNonOverride, "M01").WithArguments("C1.M01()").WithLocation(4, 24), + // (5,24): error CS0238: 'C1.P01' cannot be sealed because it is not an override + // sealed static bool P01 { get => false; } + Diagnostic(ErrorCode.ERR_SealedNonOverride, "P01").WithArguments("C1.P01").WithLocation(5, 24), + // (6,39): error CS0238: 'C1.E01' cannot be sealed because it is not an override + // sealed static event System.Action E01 { add {} remove {} } + Diagnostic(ErrorCode.ERR_SealedNonOverride, "E01").WithArguments("C1.E01").WithLocation(6, 39), + // (7,37): error CS0106: The modifier 'sealed' is not valid for this item + // public sealed static C1 operator+ (C1 x) => null; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("sealed").WithLocation(7, 37) + ); + } + } +} diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs index 12eccfd572f1d..620b8285210a6 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs @@ -1766,9 +1766,6 @@ internal int P2 { static set { } } // (3,23): error CS8503: The modifier 'public' is not valid for this item in C# 7. Please use language version '8.0' or greater. // public static int P1 { get; } Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "P1").WithArguments("public", "7.0", "8.0").WithLocation(3, 23), - // (3,28): error CS8652: The feature 'default interface implementation' is not available in C# 7. Please use language version 8.0 or greater. - // public static int P1 { get; } - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "get").WithArguments("default interface implementation", "8.0").WithLocation(3, 28), // (4,18): error CS8503: The modifier 'abstract' is not valid for this item in C# 7. Please use language version '8.0' or greater. // abstract int P2 { static set; } Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "P2").WithArguments("abstract", "7.0", "8.0").WithLocation(4, 18), @@ -2082,8 +2079,17 @@ public static int Main() } } "; - var comp = DiagnosticsUtils.VerifyErrorsAndGetCompilationWithMscorlib(text, - new ErrorDescription { Code = (int)ErrorCode.ERR_StaticNotVirtual, Line = 9, Column = 37 }); + CreateCompilation(text, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (7,18): error CS0534: 'MyClass2' does not implement inherited abstract member 'MyClass.MyMethod()' + // public class MyClass2 : MyClass + Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "MyClass2").WithArguments("MyNamespace.MyClass2", "MyNamespace.MyClass.MyMethod()").WithLocation(7, 18), + // (9,37): error CS0112: A static member cannot be marked as 'override' + // override public static void MyMethod() // CS0112, remove static keyword + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "MyMethod").WithArguments("override").WithLocation(9, 37), + // (9,37): warning CS0114: 'MyClass2.MyMethod()' hides inherited member 'MyClass.MyMethod()'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword. + // override public static void MyMethod() // CS0112, remove static keyword + Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, "MyMethod").WithArguments("MyNamespace.MyClass2.MyMethod()", "MyNamespace.MyClass.MyMethod()").WithLocation(9, 37) + ); } [Fact] @@ -2103,24 +2109,30 @@ class B : A "; var tree = Parse(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5)); CreateCompilation(tree).VerifyDiagnostics( - // (78): error CS0112: A static member 'B.P' cannot be marked as override, virtual, or abstract - // protected static override object P { get { return null; } } - Diagnostic(ErrorCode.ERR_StaticNotVirtual, "P").WithArguments("B.P").WithLocation(7, 38), - // (8,34): error CS0112: A static member 'B.Q' cannot be marked as override, virtual, or abstract - // public static virtual object Q { get; } - Diagnostic(ErrorCode.ERR_StaticNotVirtual, "Q").WithArguments("B.Q").WithLocation(8, 34), - // (8,34): error CS8026: Feature 'readonly automatically implemented properties' is not available in C# 5. Please use language version 6 or greater. - // public static virtual object Q { get; } - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion5, "Q").WithArguments("readonly automatically implemented properties", "6").WithLocation(8, 34), - // (9,37): error CS0112: A static member 'B.R' cannot be marked as override, virtual, or abstract - // internal static abstract object R { get; set; } - Diagnostic(ErrorCode.ERR_StaticNotVirtual, "R").WithArguments("B.R").WithLocation(9, 37), - // (9,41): error CS0513: 'B.R.get' is abstract but it is contained in non-abstract type 'B' - // internal static abstract object R { get; set; } - Diagnostic(ErrorCode.ERR_AbstractInConcreteClass, "get").WithArguments("B.R.get", "B").WithLocation(9, 41), - // (9,46): error CS0513: 'B.R.set' is abstract but it is contained in non-abstract type 'B' - // internal static abstract object R { get; set; } - Diagnostic(ErrorCode.ERR_AbstractInConcreteClass, "set").WithArguments("B.R.set", "B").WithLocation(9, 46)); + // (5,7): error CS0534: 'B' does not implement inherited abstract member 'A.P.get' + // class B : A + Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "B").WithArguments("B", "A.P.get").WithLocation(5, 7), + // (7,38): error CS0112: A static member cannot be marked as 'override' + // protected static override object P { get { return null; } } + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "P").WithArguments("override").WithLocation(7, 38), + // (7,38): warning CS0114: 'B.P' hides inherited member 'A.P'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword. + // protected static override object P { get { return null; } } + Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, "P").WithArguments("B.P", "A.P").WithLocation(7, 38), + // (8,34): error CS0112: A static member cannot be marked as 'virtual' + // public static virtual object Q { get; } + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "Q").WithArguments("virtual").WithLocation(8, 34), + // (8,34): error CS8026: Feature 'readonly automatically implemented properties' is not available in C# 5. Please use language version 6 or greater. + // public static virtual object Q { get; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion5, "Q").WithArguments("readonly automatically implemented properties", "6").WithLocation(8, 34), + // (9,37): error CS0112: A static member cannot be marked as 'abstract' + // internal static abstract object R { get; set; } + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "R").WithArguments("abstract").WithLocation(9, 37), + // (9,41): error CS0513: 'B.R.get' is abstract but it is contained in non-abstract type 'B' + // internal static abstract object R { get; set; } + Diagnostic(ErrorCode.ERR_AbstractInConcreteClass, "get").WithArguments("B.R.get", "B").WithLocation(9, 41), + // (9,46): error CS0513: 'B.R.set' is abstract but it is contained in non-abstract type 'B' + // internal static abstract object R { get; set; } + Diagnostic(ErrorCode.ERR_AbstractInConcreteClass, "set").WithArguments("B.R.set", "B").WithLocation(9, 46)); } [Fact] @@ -2138,12 +2150,29 @@ abstract class B : A internal static abstract event System.Action R; } "; - var comp = DiagnosticsUtils.VerifyErrorsAndGetCompilationWithMscorlib(text, - new ErrorDescription { Code = (int)ErrorCode.ERR_StaticNotVirtual, Line = 7, Column = 51 }, - new ErrorDescription { Code = (int)ErrorCode.ERR_StaticNotVirtual, Line = 8, Column = 47 }, - new ErrorDescription { Code = (int)ErrorCode.ERR_StaticNotVirtual, Line = 9, Column = 50 }, - new ErrorDescription { Code = (int)ErrorCode.WRN_UnreferencedEvent, Line = 7, Column = 51, IsWarning = true }, - new ErrorDescription { Code = (int)ErrorCode.WRN_UnreferencedEvent, Line = 8, Column = 47, IsWarning = true }); + CreateCompilation(text, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (7,51): error CS0112: A static member cannot be marked as 'override' + // protected static override event System.Action P; + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "P").WithArguments("override").WithLocation(7, 51), + // (7,51): error CS0533: 'B.P' hides inherited abstract member 'A.P' + // protected static override event System.Action P; + Diagnostic(ErrorCode.ERR_HidingAbstractMethod, "P").WithArguments("B.P", "A.P").WithLocation(7, 51), + // (7,51): warning CS0114: 'B.P' hides inherited member 'A.P'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword. + // protected static override event System.Action P; + Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, "P").WithArguments("B.P", "A.P").WithLocation(7, 51), + // (7,51): warning CS0067: The event 'B.P' is never used + // protected static override event System.Action P; + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "P").WithArguments("B.P").WithLocation(7, 51), + // (8,47): error CS0112: A static member cannot be marked as 'virtual' + // public static virtual event System.Action Q; + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "Q").WithArguments("virtual").WithLocation(8, 47), + // (8,47): warning CS0067: The event 'B.Q' is never used + // public static virtual event System.Action Q; + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "Q").WithArguments("B.Q").WithLocation(8, 47), + // (9,50): error CS0112: A static member cannot be marked as 'abstract' + // internal static abstract event System.Action R; + Diagnostic(ErrorCode.ERR_StaticNotVirtual, "R").WithArguments("abstract").WithLocation(9, 50) + ); } [Fact] From 409d2f2026e97b7d0d39ffed45feb8e1497923a9 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Fri, 2 Apr 2021 13:33:03 -0700 Subject: [PATCH 031/127] Implement several aspects of "Static Abstract Members In Interfaces" feature. (#52366) Spec: https://github.com/dotnet/csharplang/blob/3e6e0d77504b6d05eaec4232303d58b1fd806b51/proposals/statics-in-interfaces.md - Added tests to verify metadata produced for abstract static memebers in interfaces. - Added a temporary stub for the runtime support check. - Adjusted variance safety checks for signatures of abstract static members. - Adjusted generic constraints checks to disallow interfaces as type arguments for type parameters that are constrained to an interface with static abstract members. - Relaxed signature requirements for abstract user-defined operators. Relevant quotes from the spec: ## Operator restrictions Today all unary and binary operator declarations have some requirement involving at least one of their operands to be of type `T` or `T?`, where `T` is the instance type of the enclosing type. These requirements need to be relaxed so that a restricted operand is allowed to be of a type parameter that is constrained to `T`. ## Interface constraints with static abstract members Today, when an interface `I` is used as a generic constraint, any type `T` with an implicit reference or boxing conversion to `I` is considered to satisfy that constraint. When `I` has static abstract members this needs to be further restricted so that `T` cannot itself be an interface. ## Variance safety https://github.com/dotnet/csharplang/blob/main/spec/interfaces.md#variance-safety Variance safety rules should apply to signatures of static abstract members. The addition proposed in https://github.com/dotnet/csharplang/blob/main/proposals/variance-safety-for-static-interface-members.md#variance-safety should be adjusted from *These restrictions do not apply to occurrences of types within declarations of static members.* to *These restrictions do not apply to occurrences of types within declarations of **non-virtual, non-abstract** static members.* * Adjust an error message --- .../CSharp/Portable/CSharpResources.resx | 21 + .../CSharp/Portable/Errors/ErrorCode.cs | 8 + .../CSharp/Portable/Symbols/AssemblySymbol.cs | 9 + .../Portable/Symbols/ConstraintsHelper.cs | 66 +- .../Source/SourceFieldLikeEventSymbol.cs | 9 +- .../Source/SourceMemberMethodSymbol.cs | 5 +- .../SourceUserDefinedOperatorSymbolBase.cs | 32 +- .../CSharp/Portable/Symbols/VarianceSafety.cs | 4 +- .../Portable/xlf/CSharpResources.cs.xlf | 35 + .../Portable/xlf/CSharpResources.de.xlf | 35 + .../Portable/xlf/CSharpResources.es.xlf | 35 + .../Portable/xlf/CSharpResources.fr.xlf | 35 + .../Portable/xlf/CSharpResources.it.xlf | 35 + .../Portable/xlf/CSharpResources.ja.xlf | 35 + .../Portable/xlf/CSharpResources.ko.xlf | 35 + .../Portable/xlf/CSharpResources.pl.xlf | 35 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 35 + .../Portable/xlf/CSharpResources.ru.xlf | 35 + .../Portable/xlf/CSharpResources.tr.xlf | 35 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 35 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 35 + .../StaticAbstractMembersInInterfacesTests.cs | 1313 ++++++++++++++++- 22 files changed, 1904 insertions(+), 18 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 82070fe3218ca..3838d0f8897a8 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6603,4 +6603,25 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ static abstract members in interfaces + + Target runtime doesn't support static abstract members in interfaces. + + + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + + + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + + + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + + + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + + + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + + + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 8bcf3b8f3ab36..585b6b42fd8ac 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1932,6 +1932,14 @@ internal enum ErrorCode #endregion diagnostics introduced for C# 9.0 + ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces = 9100, + ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers = 9101, + ERR_BadAbstractUnaryOperatorSignature = 9102, + ERR_BadAbstractIncDecSignature = 9103, + ERR_BadAbstractIncDecRetType = 9104, + ERR_BadAbstractBinaryOperatorSignature = 9105, + ERR_BadAbstractShiftOperatorSignature = 9106, + // Note: you will need to re-generate compiler code after adding warnings (eng\generate-compiler-code.cmd) } } diff --git a/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs index 97bbfad9de5f1..9a6228caa41ac 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs @@ -431,6 +431,15 @@ internal bool RuntimeSupportsDefaultInterfaceImplementation get => RuntimeSupportsFeature(SpecialMember.System_Runtime_CompilerServices_RuntimeFeature__DefaultImplementationsOfInterfaces); } + /// + /// Figure out if the target runtime supports static abstract members in interfaces. + /// + internal bool RuntimeSupportsStaticAbstractMembersInInterfaces + { + // PROTOTYPE(StaticAbstractMembersInInterfaces): Implement the actual check, this is a temporary stub. + get => RuntimeSupportsDefaultInterfaceImplementation; + } + private bool RuntimeSupportsFeature(SpecialMember feature) { Debug.Assert((SpecialType)SpecialMembers.GetDescriptor(feature).DeclaringTypeId == SpecialType.System_Runtime_CompilerServices_RuntimeFeature); diff --git a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs index 992e26ed6b490..cc2ff5ecbcc35 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs @@ -1056,7 +1056,20 @@ private static void CheckConstraintType( ErrorCode errorCode; if (typeArgument.Type.IsReferenceType) { - errorCode = ErrorCode.ERR_GenericConstraintNotSatisfiedRefType; + // When constraint has static abstract members this needs to be further restricted so that generic argument cannot itself be an interface. + if (typeArgument.Type.IsInterfaceType() && constraintType.Type.IsInterfaceType() && + args.Conversions.WithNullability(false).HasIdentityOrImplicitReferenceConversion(typeArgument.Type, constraintType.Type, ref useSiteInfo)) + { +#if DEBUG + var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; + Debug.Assert(SelfOrBaseHasStaticAbstractMember(constraintType, ref discardedUseSiteInfo)); +#endif + errorCode = ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers; + } + else + { + errorCode = ErrorCode.ERR_GenericConstraintNotSatisfiedRefType; + } } else if (typeArgument.IsNullableType()) { @@ -1071,8 +1084,22 @@ private static void CheckConstraintType( errorCode = ErrorCode.ERR_GenericConstraintNotSatisfiedValType; } - SymbolDistinguisher distinguisher = new SymbolDistinguisher(args.CurrentCompilation, constraintType.Type, typeArgument.Type); - diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new UseSiteInfo(new CSDiagnosticInfo(errorCode, containingSymbol.ConstructedFrom(), distinguisher.First, typeParameter, distinguisher.Second)))); + object constraintTypeErrorArgument; + object typeArgumentErrorArgument; + + if (constraintType.Type.Equals(typeArgument.Type, TypeCompareKind.AllIgnoreOptions)) + { + constraintTypeErrorArgument = constraintType.Type; + typeArgumentErrorArgument = typeArgument.Type; + } + else + { + SymbolDistinguisher distinguisher = new SymbolDistinguisher(args.CurrentCompilation, constraintType.Type, typeArgument.Type); + constraintTypeErrorArgument = distinguisher.First; + typeArgumentErrorArgument = distinguisher.Second; + } + + diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new UseSiteInfo(new CSDiagnosticInfo(errorCode, containingSymbol.ConstructedFrom(), constraintTypeErrorArgument, typeParameter, typeArgumentErrorArgument)))); hasError = true; static NullableFlowState getTypeArgumentState(in TypeWithAnnotations typeWithAnnotations) @@ -1212,6 +1239,13 @@ private static bool SatisfiesConstraintType( if (conversions.HasIdentityOrImplicitReferenceConversion(typeArgument.Type, constraintType.Type, ref useSiteInfo)) { + // When constraint has static abstract members this needs to be further restricted so that generic argument cannot itself be an interface. + if (typeArgument.Type.IsInterfaceType() && constraintType.Type.IsInterfaceType() && + SelfOrBaseHasStaticAbstractMember(constraintType, ref useSiteInfo)) + { + return false; + } + return true; } @@ -1249,6 +1283,32 @@ private static bool SatisfiesConstraintType( return false; } + private static bool SelfOrBaseHasStaticAbstractMember(TypeWithAnnotations constraintType, ref CompoundUseSiteInfo useSiteInfo) + { + Debug.Assert(constraintType.Type.IsInterfaceType()); + + Func predicate = static m => m.IsStatic && m.IsAbstract; + var definition = (NamedTypeSymbol)constraintType.Type.OriginalDefinition; + + if (definition.GetMembersUnordered().Any(predicate)) + { + return true; + } + + foreach (var baseInterface in definition.InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics.Keys) + { + var baseDefinition = baseInterface.OriginalDefinition; + if (baseDefinition.GetMembersUnordered().Any(predicate)) + { + return true; + } + + baseDefinition.AddUseSiteInfo(ref useSiteInfo); + } + + return false; + } + private static bool IsReferenceType(TypeParameterSymbol typeParameter, ImmutableArray constraintTypes) { return typeParameter.HasReferenceTypeConstraint || TypeParameterSymbol.CalculateIsReferenceTypeFromConstraintTypes(constraintTypes); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldLikeEventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldLikeEventSymbol.cs index b0e5c20b6900c..09ad70771b5ac 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldLikeEventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldLikeEventSymbol.cs @@ -93,7 +93,14 @@ internal SourceFieldLikeEventSymbol(SourceMemberContainerTypeSymbol containingTy if (inInterfaceType) { - if (this.IsExtern || this.IsStatic) + if (IsAbstract && IsStatic) + { + if (!ContainingAssembly.RuntimeSupportsStaticAbstractMembersInInterfaces) + { + diagnostics.Add(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, this.Locations[0]); + } + } + else if (this.IsExtern || this.IsStatic) { if (!ContainingAssembly.RuntimeSupportsDefaultInterfaceImplementation) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs index 2c9af64aa46a1..60316e8056681 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs @@ -980,7 +980,10 @@ protected void CheckFeatureAvailabilityAndRuntimeSupport(SyntaxNode declarationS diagnostics.Add(ErrorCode.ERR_RuntimeDoesNotSupportDefaultInterfaceImplementation, location); } - // PROTOTYPE(StaticAbstractMembersInInterfaces): Check runtime capability. + if (!hasBody && IsAbstract && IsStatic && !ContainingAssembly.RuntimeSupportsStaticAbstractMembersInInterfaces) + { + diagnostics.Add(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, location); + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs index d83add495b64c..d0ee5e404850f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs @@ -497,7 +497,7 @@ private void CheckUnarySignature(BindingDiagnosticBag diagnostics) if (!MatchesContainingType(this.GetParameterType(0).StrippedType())) { // The parameter of a unary operator must be the containing type - diagnostics.Add(ErrorCode.ERR_BadUnaryOperatorSignature, this.Locations[0]); + diagnostics.Add(IsAbstract ? ErrorCode.ERR_BadAbstractUnaryOperatorSignature : ErrorCode.ERR_BadUnaryOperatorSignature, this.Locations[0]); } if (this.ReturnsVoid) @@ -522,7 +522,7 @@ private void CheckTrueFalseSignature(BindingDiagnosticBag diagnostics) if (!MatchesContainingType(this.GetParameterType(0).StrippedType())) { // The parameter of a unary operator must be the containing type - diagnostics.Add(ErrorCode.ERR_BadUnaryOperatorSignature, this.Locations[0]); + diagnostics.Add(IsAbstract ? ErrorCode.ERR_BadAbstractUnaryOperatorSignature : ErrorCode.ERR_BadUnaryOperatorSignature, this.Locations[0]); } } @@ -572,23 +572,41 @@ private void CheckIncrementDecrementSignature(BindingDiagnosticBag diagnostics) if (!MatchesContainingType(parameterType.StrippedType())) { // CS0559: The parameter type for ++ or -- operator must be the containing type - diagnostics.Add(ErrorCode.ERR_BadIncDecSignature, this.Locations[0]); + diagnostics.Add(IsAbstract ? ErrorCode.ERR_BadAbstractIncDecSignature : ErrorCode.ERR_BadIncDecSignature, this.Locations[0]); } - else if (!this.ReturnType.EffectiveTypeNoUseSiteDiagnostics.IsEqualToOrDerivedFrom(parameterType, ComparisonForUserDefinedOperators, useSiteInfo: ref useSiteInfo)) + else if (!(parameterType.IsTypeParameter() ? + this.ReturnType.Equals(parameterType, ComparisonForUserDefinedOperators) : + ((IsAbstract && IsContainingType(parameterType) && IsSelfConstrainedTypeParameter(this.ReturnType)) || + this.ReturnType.EffectiveTypeNoUseSiteDiagnostics.IsEqualToOrDerivedFrom(parameterType, ComparisonForUserDefinedOperators, useSiteInfo: ref useSiteInfo)))) { // CS0448: The return type for ++ or -- operator must match the parameter type // or be derived from the parameter type - diagnostics.Add(ErrorCode.ERR_BadIncDecRetType, this.Locations[0]); + diagnostics.Add(IsAbstract ? ErrorCode.ERR_BadAbstractIncDecRetType : ErrorCode.ERR_BadIncDecRetType, this.Locations[0]); } diagnostics.Add(this.Locations[0], useSiteInfo); } private bool MatchesContainingType(TypeSymbol type) + { + return IsContainingType(type) || (IsAbstract && IsSelfConstrainedTypeParameter(type)); + } + + private bool IsContainingType(TypeSymbol type) { return type.Equals(this.ContainingType, ComparisonForUserDefinedOperators); } + private bool IsSelfConstrainedTypeParameter(TypeSymbol type) + { + return type is TypeParameterSymbol p && + // PROTOTYPE(StaticAbstractMembersInInterfaces): For now assuming the type parameter must belong to the containing type. + (object)p.ContainingSymbol == this.ContainingType && + // PROTOTYPE(StaticAbstractMembersInInterfaces): For now assume containing type must be one of the directly specified constraints. + p.ConstraintTypesNoUseSiteDiagnostics.Any((typeArgument, containingType) => typeArgument.Type.Equals(containingType, ComparisonForUserDefinedOperators), + this.ContainingType); + } + private void CheckShiftSignature(BindingDiagnosticBag diagnostics) { // SPEC: A binary << or >> operator must take two parameters, the first @@ -601,7 +619,7 @@ private void CheckShiftSignature(BindingDiagnosticBag diagnostics) // CS0546: The first operand of an overloaded shift operator must have the // same type as the containing type, and the type of the second // operand must be int - diagnostics.Add(ErrorCode.ERR_BadShiftOperatorSignature, this.Locations[0]); + diagnostics.Add(IsAbstract ? ErrorCode.ERR_BadAbstractShiftOperatorSignature : ErrorCode.ERR_BadShiftOperatorSignature, this.Locations[0]); } if (this.ReturnsVoid) @@ -620,7 +638,7 @@ private void CheckBinarySignature(BindingDiagnosticBag diagnostics) !MatchesContainingType(this.GetParameterType(1).StrippedType())) { // CS0563: One of the parameters of a binary operator must be the containing type - diagnostics.Add(ErrorCode.ERR_BadBinaryOperatorSignature, this.Locations[0]); + diagnostics.Add(IsAbstract ? ErrorCode.ERR_BadAbstractBinaryOperatorSignature : ErrorCode.ERR_BadBinaryOperatorSignature, this.Locations[0]); } if (this.ReturnsVoid) diff --git a/src/Compilers/CSharp/Portable/Symbols/VarianceSafety.cs b/src/Compilers/CSharp/Portable/Symbols/VarianceSafety.cs index 5fb05d5dbe891..81252677ebf21 100644 --- a/src/Compilers/CSharp/Portable/Symbols/VarianceSafety.cs +++ b/src/Compilers/CSharp/Portable/Symbols/VarianceSafety.cs @@ -171,7 +171,7 @@ private static void CheckMethodVarianceSafety(this MethodSymbol method, Location private static bool SkipVarianceSafetyChecks(Symbol member) { - if (member.IsStatic) + if (member.IsStatic && !member.IsAbstract) { return MessageID.IDS_FeatureVarianceSafetyForStaticInterfaceMembers.RequiredVersion() <= member.DeclaringCompilation.LanguageVersion; } @@ -469,7 +469,7 @@ private static void AddVarianceError( // "requires output-safe", and "requires input-safe and output-safe". This would make the error codes much easier to document and // much more actionable. // UNDONE: related location for use is much more useful - if (!(context is TypeSymbol) && context.IsStatic) + if (!(context is TypeSymbol) && context.IsStatic && !context.IsAbstract) { diagnostics.Add(ErrorCode.ERR_UnexpectedVarianceStaticMember, location, context, unsafeTypeParameter, actualVariance.Localize(), expectedVariance.Localize(), new CSharpRequiredLanguageVersion(MessageID.IDS_FeatureVarianceSafetyForStaticInterfaceMembers.RequiredVersion())); diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index ed9f6601c63af..b4804b329acc7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -97,6 +97,31 @@ Asynchronní příkaz foreach nejde použít pro proměnné typu {0}, protože {0} neobsahuje veřejnou definici instance nebo rozšíření pro {1}. Měli jste v úmyslu foreach místo await foreach? + + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + + + + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + + + + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + + + + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + + + + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + + Cannot use a collection of dynamic type in an asynchronous foreach V asynchronním příkazu foreach nejde použít kolekce dynamického typu. @@ -437,6 +462,11 @@ Ukazatel na funkci se nedá zavolat s pojmenovanými argumenty. + + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + + A goto cannot jump to a location before a using declaration within the same block. Příkaz goto nemůže přejít na místo před deklarací using ve stejném bloku. @@ -822,6 +852,11 @@ Cílový modul runtime nepodporuje pro člena rozhraní přístupnost na úrovni Protected, Protected internal nebo Private protected. + + Target runtime doesn't support static abstract members in interfaces. + Target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. Cílový modul runtime nepodporuje rozšiřitelné konvence volání ani konvence volání výchozí pro prostředí modulu runtime. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index d03315d9d1f47..7c8a8c48a00cb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -97,6 +97,31 @@ Eine asynchrone foreach-Anweisung kann nicht für Variablen vom Typ "{0}" verwendet werden, weil "{0}" keine öffentliche Instanz- oder Erweiterungsdefinition für "{1}" enthält. Meinten Sie "foreach" statt "await foreach"? + + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + + + + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + + + + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + + + + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + + + + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + + Cannot use a collection of dynamic type in an asynchronous foreach Eine Sammlung des dynamic-Typs kann in einem asynchronen foreach nicht verwendet werden. @@ -437,6 +462,11 @@ Ein Funktionszeiger kann nicht mit benannten Argumenten aufgerufen werden. + + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + + A goto cannot jump to a location before a using declaration within the same block. Mit "goto" kann nicht an eine Position vor einer using-Deklaration im selben Block gesprungen werden. @@ -822,6 +852,11 @@ Die Zugriffsoptionen "protected", "protected internal" oder "private protected" werden von der Zielruntime für einen Member einer Schnittstelle nicht unterstützt. + + Target runtime doesn't support static abstract members in interfaces. + Target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. Die Zielruntime unterstützt keine erweiterbaren Aufrufkonventionen oder Standardaufrufkonventionen der Runtime-Umgebung. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 44f634d570134..f2f9cc202e49e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -97,6 +97,31 @@ Una instrucción foreach asincrónica no puede funcionar en variables de tipo "{0}" porque "{0}" no contiene ninguna definición de extensión o instancia pública para "{1}". ¿Quiso decir “foreach” en lugar de “await foreach”? + + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + + + + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + + + + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + + + + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + + + + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + + Cannot use a collection of dynamic type in an asynchronous foreach No se puede usar una colección de tipo dinámico en una instrucción foreach asincrónica. @@ -437,6 +462,11 @@ No se puede llamar a un puntero a función con argumentos con nombre. + + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + + A goto cannot jump to a location before a using declaration within the same block. Una instrucción goto no puede saltar a una ubicación antes que una declaración using dentro del mismo bloque. @@ -822,6 +852,11 @@ El entorno de ejecución de destino no admite la accesibilidad protegida, protegida interna o protegida privada para un miembro de una interfaz. + + Target runtime doesn't support static abstract members in interfaces. + Target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. El entorno de ejecución de destino no admite convenciones de llamada predeterminadas de entorno en tiempo de ejecución o extensible. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 6d67767d00e8c..2aff1c95aa07f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -97,6 +97,31 @@ L'instruction foreach asynchrone ne peut pas fonctionner sur des variables de type '{0}', car '{0}' ne contient pas de définition d'extension ou d'instance publique pour '{1}'. Vouliez-vous dire 'foreach' plutôt que 'await foreach' ? + + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + + + + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + + + + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + + + + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + + + + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + + Cannot use a collection of dynamic type in an asynchronous foreach Impossible d'utiliser une collection de type dynamique dans un foreach asynchrone @@ -437,6 +462,11 @@ Impossible d'appeler un pointeur de fonction avec des arguments nommés. + + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + + A goto cannot jump to a location before a using declaration within the same block. Un goto ne peut pas accéder à un emplacement avant une déclaration using dans le même bloc. @@ -822,6 +852,11 @@ Le runtime cible ne prend pas en charge l'accessibilité 'protected', 'protected internal' ou 'private protected' d'un membre d'interface. + + Target runtime doesn't support static abstract members in interfaces. + Target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. Le runtime cible ne prend pas en charge les conventions d'appel par défaut des environnements extensibles ou d'exécution. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 8788fcdda7fa5..95bbf96999aba 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -97,6 +97,31 @@ L'istruzione foreach asincrona non può funzionare con variabili di tipo '{0}' perché '{0}' non contiene una definizione di istanza o estensione pubblica per '{1}'. Si intendeva 'foreach' invece di 'await foreach'? + + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + + + + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + + + + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + + + + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + + + + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + + Cannot use a collection of dynamic type in an asynchronous foreach Non è possibile usare una raccolta di tipo dinamico in un'istruzione foreach asincrona @@ -437,6 +462,11 @@ Non è possibile chiamare un puntatore a funzione con argomenti denominati. + + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + + A goto cannot jump to a location before a using declaration within the same block. Un'istruzione goto non può passare a una posizione che precede una dichiarazione using all'interno dello stesso blocco. @@ -822,6 +852,11 @@ Il runtime di destinazione non supporta l'accessibilità 'protected', 'protected internal' o 'private protected' per un membro di un'interfaccia. + + Target runtime doesn't support static abstract members in interfaces. + Target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. Il runtime di destinazione non supporta convenzioni di chiamata predefinite estendibili o dell'ambiente di runtime. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 4e5d10185fbba..81256aee8e0f6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -97,6 +97,31 @@ '{0}' は '{1}' のパブリック インスタンスまたは拡張機能の定義を含んでいないため、型 '{0}' の変数に対して非同期 foreach ステートメントを使用することはできません。'await foreach' ではなく 'foreach' ですか? + + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + + + + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + + + + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + + + + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + + + + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + + Cannot use a collection of dynamic type in an asynchronous foreach 非同期 foreach では動的な型のコレクションを使用できません @@ -437,6 +462,11 @@ 関数ポインターを名前付き引数で呼び出すことはできません。 + + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + + A goto cannot jump to a location before a using declaration within the same block. goto は同じブロック内の using 宣言より前の位置にはジャンプできません。 @@ -822,6 +852,11 @@ ターゲット ランタイムは、インターフェイスのメンバーに対して 'protected'、'protected internal'、'private protected' アクセシビリティをサポートしていません。 + + Target runtime doesn't support static abstract members in interfaces. + Target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. ターゲット ランタイムは、拡張可能またはランタイム環境の既定の呼び出し規則をサポートしていません。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index d2c8899d066c0..4824f2148b665 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -97,6 +97,31 @@ '{0}' 형식 변수에서 비동기 foreach 문을 수행할 수 없습니다. '{0}'에는 '{1}'의 공개 인스턴스 또는 확장 정의가 없기 때문입니다. 'await foreach' 대신 'foreach'를 사용하시겠습니까? + + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + + + + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + + + + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + + + + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + + + + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + + Cannot use a collection of dynamic type in an asynchronous foreach 비동기 foreach에는 동적 형식 컬렉션을 사용할 수 없습니다. @@ -437,6 +462,11 @@ 함수 포인터는 명명된 인수를 사용하여 호출할 수 없습니다. + + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + + A goto cannot jump to a location before a using declaration within the same block. goto는 동일한 블록 내의 using 선언 앞 위치로 이동할 수 없습니다. @@ -822,6 +852,11 @@ 대상 런타임이 인터페이스 멤버의 'protected', 'protected internal' 또는 'private protected' 접근성을 지원하지 않습니다. + + Target runtime doesn't support static abstract members in interfaces. + Target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. 대상 런타임에서 확장 가능 또는 런타임 환경 기본 호출 규칙을 지원하지 않습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index f777be11c85a6..b9772c254abf7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -97,6 +97,31 @@ Asynchroniczna instrukcja foreach nie może operować na zmiennych typu „{0}”, ponieważ typ „{0}” nie zawiera publicznego wystąpienia lub definicji rozszerzenia dla elementu „{1}”. Czy planowano użyć instrukcji „foreach”, a nie „await foreach”? + + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + + + + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + + + + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + + + + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + + + + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + + Cannot use a collection of dynamic type in an asynchronous foreach Nie można użyć kolekcji typu dynamicznego w asynchronicznej instrukcji foreach @@ -437,6 +462,11 @@ Nie można wywołać wskaźnika funkcji przy użyciu argumentów nazwanych. + + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + + A goto cannot jump to a location before a using declaration within the same block. Instrukcja goto nie może przechodzić do lokalizacji występującej przed deklaracją using w tym samym bloku. @@ -822,6 +852,11 @@ Docelowe środowisko uruchomieniowe nie obsługuje specyfikatorów dostępu „protected”, „protected internal” i „private protected” dla składowej interfejsu. + + Target runtime doesn't support static abstract members in interfaces. + Target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. Docelowe środowisko uruchomieniowe nie obsługuje rozszerzalnych ani domyślnych dla środowiska uruchomieniowego konwencji wywoływania. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index ba4ee95ae89e3..1ffbdfc2e8b99 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -97,6 +97,31 @@ A instrução foreach assíncrona não pode operar em variáveis do tipo '{0}' porque '{0}' não contém uma definição de extensão ou de instância pública para '{1}'. Você quis dizer 'foreach' em vez de 'await foreach'? + + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + + + + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + + + + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + + + + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + + + + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + + Cannot use a collection of dynamic type in an asynchronous foreach Não é possível usar uma coleção do tipo dinâmico em uma foreach assíncrona @@ -437,6 +462,11 @@ Um ponteiro de função não pode ser chamado com argumentos nomeados. + + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + + A goto cannot jump to a location before a using declaration within the same block. Um goto não pode saltar para um local antes de uma declaração using no mesmo bloco. @@ -822,6 +852,11 @@ O runtime de destino não é compatível com a acessibilidade 'protected', 'protected internal' ou 'private protected' para um membro de uma interface. + + Target runtime doesn't support static abstract members in interfaces. + Target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. O runtime de destino não dá suporte a convenções de chamada padrão extensíveis ou de ambiente de runtime. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index fc49fb2900369..57148eea063f2 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -97,6 +97,31 @@ Асинхронный оператор foreach не работает с переменными типа "{0}", так как "{0}" не содержит открытое определение экземпляра или расширения для "{1}" Возможно, вы имели в виду "foreach", а не "await foreach"? + + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + + + + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + + + + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + + + + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + + + + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + + Cannot use a collection of dynamic type in an asynchronous foreach Не удается использовать коллекцию динамического типа в асинхронном операторе foreach @@ -437,6 +462,11 @@ Невозможно вызвать указатель на функцию с именованными аргументами. + + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + + A goto cannot jump to a location before a using declaration within the same block. Оператор goto не может переходить к расположению раньше объявления using в том же блоке. @@ -822,6 +852,11 @@ Целевая среда выполнения не поддерживает специальные возможности "защищенный", "внутренний защищенный" или "частный защищенный" для члена интерфейса. + + Target runtime doesn't support static abstract members in interfaces. + Target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. Целевая среда выполнения не поддерживает расширяемые или принадлежащие среде выполнения соглашения о вызовах по умолчанию. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index f17819d3f997e..7d0d3abb7ed85 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -97,6 +97,31 @@ '{0}', '{1}' için bir genel örnek veya uzantı tanımı içermediğinden zaman uyumsuz foreach deyimi '{0}' türündeki değişkenler üzerinde çalışamaz. 'await foreach' yerine 'foreach' mi kullanmak istediniz? + + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + + + + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + + + + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + + + + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + + + + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + + Cannot use a collection of dynamic type in an asynchronous foreach Zaman uyumsuz bir foreach içinde dinamik tür koleksiyonu oluşturulamaz @@ -437,6 +462,11 @@ İşlev işaretçisi, adlandırılmış bağımsız değişkenler ile çağrılamaz. + + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + + A goto cannot jump to a location before a using declaration within the same block. Bir goto, aynı blok içinde yer alan using bildiriminden önceki bir konuma atlayamaz. @@ -822,6 +852,11 @@ Hedef çalışma zamanı, bir arabirim üyesi için 'protected', 'protected internal' veya 'private protected' erişilebilirliğini desteklemez. + + Target runtime doesn't support static abstract members in interfaces. + Target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. Hedef çalışma zamanı, genişletilebilir veya çalışma zamanı ortamı varsayılanı çağırma kurallarını desteklemiyor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index a34f064fe0c87..efb912e165b16 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -97,6 +97,31 @@ “{0}”不包含“{1}”的公共实例或扩展定义,因此异步 foreach 语句不能作用于“{0}”类型的变量。是否希望使用 "foreach" 而非 "await foreach"? + + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + + + + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + + + + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + + + + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + + + + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + + Cannot use a collection of dynamic type in an asynchronous foreach 无法在异步 foreach 中使用动态类型集合 @@ -437,6 +462,11 @@ 不能使用命名参数调用函数指针。 + + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + + A goto cannot jump to a location before a using declaration within the same block. goto 无法跳转到同一块中 using 声明之前的某个位置。 @@ -822,6 +852,11 @@ 目标运行时不支持对接口的成员使用 "protected"、"protected internal" 或 "private protected" 辅助功能。 + + Target runtime doesn't support static abstract members in interfaces. + Target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. 目标运行时不支持可扩展或运行时环境默认调用约定。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 5a4d82220ab12..24de2372d2819 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -97,6 +97,31 @@ 因為 '{0}' 不包含 '{1}' 的公用執行個體或延伸模組定義,所以非同步的 foreach 陳述式無法在型別 '{0}' 的變數上運作。您指的是 'foreach' 而不是 'await foreach' 嗎? + + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + + + + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + + + + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + + + + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + + + + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + + Cannot use a collection of dynamic type in an asynchronous foreach 無法在非同步 foreach 中使用動態類型的集合 @@ -437,6 +462,11 @@ 無法以具名引數呼叫函式指標。 + + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members. + + A goto cannot jump to a location before a using declaration within the same block. 在相同區塊內,goto 不可跳到 using 宣告前的位置。 @@ -822,6 +852,11 @@ 目標執行階段不支援介面成員的 'protected'、'protected internal' 或 'private protected' 存取權。 + + Target runtime doesn't support static abstract members in interfaces. + Target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. 目標執行階段不支援可延伸或執行階段環境的預設呼叫慣例。 diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index fb60e77189c17..28ab5f8652417 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -4110,16 +4110,16 @@ struct C1 } [Fact] - public void SealedStaticInstruct_01() + public void SealedStaticInStruct_01() { var source1 = @" -class C1 +struct C1 { sealed static void M01() {} sealed static bool P01 { get => false; } sealed static event System.Action E01 { add {} remove {} } - public sealed static C1 operator+ (C1 x) => null; + public sealed static C1 operator+ (C1 x) => default; } "; var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, @@ -4137,9 +4137,1314 @@ sealed static event System.Action E01 { add {} remove {} } // sealed static event System.Action E01 { add {} remove {} } Diagnostic(ErrorCode.ERR_SealedNonOverride, "E01").WithArguments("C1.E01").WithLocation(6, 39), // (7,37): error CS0106: The modifier 'sealed' is not valid for this item - // public sealed static C1 operator+ (C1 x) => null; + // public sealed static C1 operator+ (C1 x) => default; Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("sealed").WithLocation(7, 37) ); } + + [Fact] + public void DefineAbstractStaticMethod_01() + { + var source1 = +@" +interface I1 +{ + abstract static void M01(); +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var m01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); + + Assert.True(m01.IsMetadataNewSlot()); + Assert.True(m01.IsAbstract); + Assert.True(m01.IsMetadataVirtual()); + Assert.False(m01.IsMetadataFinal); + Assert.False(m01.IsVirtual); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsOverride); + } + } + + [Fact] + public void DefineAbstractStaticMethod_02() + { + var source1 = +@" +interface I1 +{ + abstract static void M01(); +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation1.VerifyDiagnostics( + // (4,26): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static void M01(); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(4, 26) + ); + } + + [Theory] + [InlineData("I1", "+", "(I1 x)")] + [InlineData("I1", "-", "(I1 x)")] + [InlineData("I1", "!", "(I1 x)")] + [InlineData("I1", "~", "(I1 x)")] + [InlineData("I1", "++", "(I1 x)")] + [InlineData("I1", "--", "(I1 x)")] + [InlineData("I1", "+", "(I1 x, I1 y)")] + [InlineData("I1", "-", "(I1 x, I1 y)")] + [InlineData("I1", "*", "(I1 x, I1 y)")] + [InlineData("I1", "/", "(I1 x, I1 y)")] + [InlineData("I1", "%", "(I1 x, I1 y)")] + [InlineData("I1", "&", "(I1 x, I1 y)")] + [InlineData("I1", "|", "(I1 x, I1 y)")] + [InlineData("I1", "^", "(I1 x, I1 y)")] + [InlineData("I1", "<<", "(I1 x, int y)")] + [InlineData("I1", ">>", "(I1 x, int y)")] + public void DefineAbstractStaticOperator_01(string type, string op, string paramList) + { + var source1 = +@" +interface I1 +{ + abstract static " + type + " operator " + op + " " + paramList + @"; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var m01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); + + Assert.True(m01.IsMetadataNewSlot()); + Assert.True(m01.IsAbstract); + Assert.True(m01.IsMetadataVirtual()); + Assert.False(m01.IsMetadataFinal); + Assert.False(m01.IsVirtual); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsOverride); + } + } + + [Fact] + public void DefineAbstractStaticOperator_02() + { + var source1 = +@" +interface I1 +{ + abstract static bool operator true (I1 x); + abstract static bool operator false (I1 x); + abstract static I1 operator > (I1 x, I1 y); + abstract static I1 operator < (I1 x, I1 y); + abstract static I1 operator >= (I1 x, I1 y); + abstract static I1 operator <= (I1 x, I1 y); +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + int count = 0; + foreach (var m01 in module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType()) + { + Assert.True(m01.IsMetadataNewSlot()); + Assert.True(m01.IsAbstract); + Assert.True(m01.IsMetadataVirtual()); + Assert.False(m01.IsMetadataFinal); + Assert.False(m01.IsVirtual); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsOverride); + + count++; + } + + Assert.Equal(6, count); + } + } + + [Theory] + [InlineData("I1", "+", "(I1 x)")] + [InlineData("I1", "-", "(I1 x)")] + [InlineData("I1", "!", "(I1 x)")] + [InlineData("I1", "~", "(I1 x)")] + [InlineData("I1", "++", "(I1 x)")] + [InlineData("I1", "--", "(I1 x)")] + [InlineData("I1", "+", "(I1 x, I1 y)")] + [InlineData("I1", "-", "(I1 x, I1 y)")] + [InlineData("I1", "*", "(I1 x, I1 y)")] + [InlineData("I1", "/", "(I1 x, I1 y)")] + [InlineData("I1", "%", "(I1 x, I1 y)")] + [InlineData("I1", "&", "(I1 x, I1 y)")] + [InlineData("I1", "|", "(I1 x, I1 y)")] + [InlineData("I1", "^", "(I1 x, I1 y)")] + [InlineData("I1", "<<", "(I1 x, int y)")] + [InlineData("I1", ">>", "(I1 x, int y)")] + public void DefineAbstractStaticOperator_03(string type, string op, string paramList) + { + var source1 = +@" +interface I1 +{ + abstract static " + type + " operator " + op + " " + paramList + @"; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation1.VerifyDiagnostics( + // (4,33): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static I1 operator + (I1 x); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(4, 31 + type.Length) + ); + } + + [Fact] + public void DefineAbstractStaticOperator_04() + { + var source1 = +@" +interface I1 +{ + abstract static bool operator true (I1 x); + abstract static bool operator false (I1 x); + abstract static I1 operator > (I1 x, I1 y); + abstract static I1 operator < (I1 x, I1 y); + abstract static I1 operator >= (I1 x, I1 y); + abstract static I1 operator <= (I1 x, I1 y); +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation1.VerifyDiagnostics( + // (4,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static bool operator true (I1 x); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "true").WithLocation(4, 35), + // (5,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static bool operator false (I1 x); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "false").WithLocation(5, 35), + // (6,33): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static I1 operator > (I1 x, I1 y); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, ">").WithLocation(6, 33), + // (7,33): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static I1 operator < (I1 x, I1 y); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "<").WithLocation(7, 33), + // (8,33): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static I1 operator >= (I1 x, I1 y); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, ">=").WithLocation(8, 33), + // (9,33): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static I1 operator <= (I1 x, I1 y); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "<=").WithLocation(9, 33) + ); + } + + [Fact] + public void DefineAbstractStaticProperty_01() + { + var source1 = +@" +interface I1 +{ + abstract static int P01 { get; set; } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var p01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); + + Assert.True(p01.IsAbstract); + Assert.False(p01.IsVirtual); + Assert.False(p01.IsSealed); + Assert.True(p01.IsStatic); + Assert.False(p01.IsOverride); + + int count = 0; + foreach (var m01 in module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType()) + { + Assert.True(m01.IsMetadataNewSlot()); + Assert.True(m01.IsAbstract); + Assert.True(m01.IsMetadataVirtual()); + Assert.False(m01.IsMetadataFinal); + Assert.False(m01.IsVirtual); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsOverride); + + count++; + } + + Assert.Equal(2, count); + } + } + + [Fact] + public void DefineAbstractStaticProperty_02() + { + var source1 = +@" +interface I1 +{ + abstract static int P01 { get; set; } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation1.VerifyDiagnostics( + // (4,31): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static int P01 { get; set; } + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "get").WithLocation(4, 31), + // (4,36): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static int P01 { get; set; } + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "set").WithLocation(4, 36) + ); + } + + [Fact] + public void DefineAbstractStaticEvent_01() + { + var source1 = +@" +interface I1 +{ + abstract static event System.Action E01; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var e01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); + + Assert.True(e01.IsAbstract); + Assert.False(e01.IsVirtual); + Assert.False(e01.IsSealed); + Assert.True(e01.IsStatic); + Assert.False(e01.IsOverride); + + int count = 0; + foreach (var m01 in module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType()) + { + Assert.True(m01.IsMetadataNewSlot()); + Assert.True(m01.IsAbstract); + Assert.True(m01.IsMetadataVirtual()); + Assert.False(m01.IsMetadataFinal); + Assert.False(m01.IsVirtual); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsOverride); + + count++; + } + + Assert.Equal(2, count); + } + } + + [Fact] + public void DefineAbstractStaticEvent_02() + { + var source1 = +@" +interface I1 +{ + abstract static event System.Action E01; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation1.VerifyDiagnostics( + // (4,41): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static event System.Action E01; + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "E01").WithLocation(4, 41) + ); + } + + [Fact] + public void ConstraintChecks_01() + { + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} + +public interface I2 : I1 +{ +} + +public interface I3 : I2 +{ +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var source2 = +@" +class C1 where T1 : I1 +{ + void Test(C1 x) + { + } +} + +class C2 +{ + void M() where T2 : I1 {} + + void Test(C2 x) + { + x.M(); + } +} + +class C3 where T3 : I2 +{ + void Test(C3 x, C3 y) + { + } +} + +class C4 +{ + void M() where T4 : I2 {} + + void Test(C4 x) + { + x.M(); + x.M(); + } +} + +class C5 where T5 : I3 +{ + void Test(C5 y) + { + } +} + +class C6 +{ + void M() where T6 : I3 {} + + void Test(C6 x) + { + x.M(); + } +} + +class C7 where T7 : I1 +{ + void Test(C7 y) + { + } +} + +class C8 +{ + void M() where T8 : I1 {} + + void Test(C8 x) + { + x.M(); + } +} +"; + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + var expected = new[] { + // (4,22): error CS9101: The interface 'I2' cannot be used as type parameter 'T1' in the generic type or method 'C1'. The constraint interface 'I1' or its base interface has static abstract members. + // void Test(C1 x) + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "x").WithArguments("C1", "I1", "T1", "I2").WithLocation(4, 22), + // (15,11): error CS9101: The interface 'I2' cannot be used as type parameter 'T2' in the generic type or method 'C2.M()'. The constraint interface 'I1' or its base interface has static abstract members. + // x.M(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "M").WithArguments("C2.M()", "I1", "T2", "I2").WithLocation(15, 11), + // (21,22): error CS9101: The interface 'I2' cannot be used as type parameter 'T3' in the generic type or method 'C3'. The constraint interface 'I2' or its base interface has static abstract members. + // void Test(C3 x, C3 y) + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "x").WithArguments("C3", "I2", "T3", "I2").WithLocation(21, 22), + // (21,32): error CS9101: The interface 'I3' cannot be used as type parameter 'T3' in the generic type or method 'C3'. The constraint interface 'I2' or its base interface has static abstract members. + // void Test(C3 x, C3 y) + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "y").WithArguments("C3", "I2", "T3", "I3").WithLocation(21, 32), + // (32,11): error CS9101: The interface 'I2' cannot be used as type parameter 'T4' in the generic type or method 'C4.M()'. The constraint interface 'I2' or its base interface has static abstract members. + // x.M(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "M").WithArguments("C4.M()", "I2", "T4", "I2").WithLocation(32, 11), + // (33,11): error CS9101: The interface 'I3' cannot be used as type parameter 'T4' in the generic type or method 'C4.M()'. The constraint interface 'I2' or its base interface has static abstract members. + // x.M(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "M").WithArguments("C4.M()", "I2", "T4", "I3").WithLocation(33, 11), + // (39,22): error CS9101: The interface 'I3' cannot be used as type parameter 'T5' in the generic type or method 'C5'. The constraint interface 'I3' or its base interface has static abstract members. + // void Test(C5 y) + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "y").WithArguments("C5", "I3", "T5", "I3").WithLocation(39, 22), + // (50,11): error CS9101: The interface 'I3' cannot be used as type parameter 'T6' in the generic type or method 'C6.M()'. The constraint interface 'I3' or its base interface has static abstract members. + // x.M(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "M").WithArguments("C6.M()", "I3", "T6", "I3").WithLocation(50, 11), + // (56,22): error CS9101: The interface 'I1' cannot be used as type parameter 'T7' in the generic type or method 'C7'. The constraint interface 'I1' or its base interface has static abstract members. + // void Test(C7 y) + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "y").WithArguments("C7", "I1", "T7", "I1").WithLocation(56, 22), + // (67,11): error CS9101: The interface 'I1' cannot be used as type parameter 'T8' in the generic type or method 'C8.M()'. The constraint interface 'I1' or its base interface has static abstract members. + // x.M(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "M").WithArguments("C8.M()", "I1", "T8", "I1").WithLocation(67, 11) + }; + + compilation2.VerifyDiagnostics(expected); + + compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.EmitToImageReference() }); + + compilation2.VerifyDiagnostics(expected); + } + + [Fact] + public void ConstraintChecks_02() + { + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} + +public class C : I1 +{ + public static void M01() {} +} + +public struct S : I1 +{ + public static void M01() {} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var source2 = +@" +class C1 where T1 : I1 +{ + void Test(C1 x, C1 y, C1 z) + { + } +} + +class C2 +{ + public void M(C2 x) where T2 : I1 + { + x.M(x); + } + + void Test(C2 x) + { + x.M(x); + x.M(x); + } +} + +class C3 where T3 : I1 +{ + void Test(C1 z) + { + } +} + +class C4 +{ + void M(C2 x) where T4 : I1 + { + x.M(x); + } +} + +class C5 +{ + internal virtual void M() where U5 : T5 { } +} + +class C6 : C5 +{ + internal override void M() { base.M(); } +} +"; + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyEmitDiagnostics(); + + compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.EmitToImageReference() }); + + compilation2.VerifyEmitDiagnostics(); + } + + [Fact] + public void VarianceSafety_01() + { + var source1 = +@" +interface I2 +{ + abstract static T1 P1 { get; } + abstract static T2 P2 { get; } + abstract static T1 P3 { set; } + abstract static T2 P4 { set; } +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + compilation1.VerifyDiagnostics( + // (5,21): error CS1961: Invalid variance: The type parameter 'T2' must be covariantly valid on 'I2.P2'. 'T2' is contravariant. + // abstract static T2 P2 { get; } + Diagnostic(ErrorCode.ERR_UnexpectedVariance, "T2").WithArguments("I2.P2", "T2", "contravariant", "covariantly").WithLocation(5, 21), + // (6,21): error CS1961: Invalid variance: The type parameter 'T1' must be contravariantly valid on 'I2.P3'. 'T1' is covariant. + // abstract static T1 P3 { set; } + Diagnostic(ErrorCode.ERR_UnexpectedVariance, "T1").WithArguments("I2.P3", "T1", "covariant", "contravariantly").WithLocation(6, 21) + ); + } + + [Fact] + public void VarianceSafety_02() + { + var source1 = +@" +interface I2 +{ + abstract static T1 M1(); + abstract static T2 M2(); + abstract static void M3(T1 x); + abstract static void M4(T2 x); +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + compilation1.VerifyDiagnostics( + // (5,21): error CS1961: Invalid variance: The type parameter 'T2' must be covariantly valid on 'I2.M2()'. 'T2' is contravariant. + // abstract static T2 M2(); + Diagnostic(ErrorCode.ERR_UnexpectedVariance, "T2").WithArguments("I2.M2()", "T2", "contravariant", "covariantly").WithLocation(5, 21), + // (6,29): error CS1961: Invalid variance: The type parameter 'T1' must be contravariantly valid on 'I2.M3(T1)'. 'T1' is covariant. + // abstract static void M3(T1 x); + Diagnostic(ErrorCode.ERR_UnexpectedVariance, "T1").WithArguments("I2.M3(T1)", "T1", "covariant", "contravariantly").WithLocation(6, 29) + ); + } + + [Fact] + public void VarianceSafety_03() + { + var source1 = +@" +interface I2 +{ + abstract static event System.Action> E1; + abstract static event System.Action> E2; + abstract static event System.Action> E3; + abstract static event System.Action> E4; +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + compilation1.VerifyDiagnostics( + // (5,58): error CS1961: Invalid variance: The type parameter 'T2' must be covariantly valid on 'I2.E2'. 'T2' is contravariant. + // abstract static event System.Action> E2; + Diagnostic(ErrorCode.ERR_UnexpectedVariance, "E2").WithArguments("I2.E2", "T2", "contravariant", "covariantly").WithLocation(5, 58), + // (6,60): error CS1961: Invalid variance: The type parameter 'T1' must be contravariantly valid on 'I2.E3'. 'T1' is covariant. + // abstract static event System.Action> E3; + Diagnostic(ErrorCode.ERR_UnexpectedVariance, "E3").WithArguments("I2.E3", "T1", "covariant", "contravariantly").WithLocation(6, 60) + ); + } + + [Fact] + public void VarianceSafety_04() + { + var source1 = +@" +interface I2 +{ + abstract static int operator +(I2 x); +} + +interface I3 +{ + abstract static int operator +(I3 x); +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + compilation1.VerifyDiagnostics( + // (4,36): error CS1961: Invalid variance: The type parameter 'T2' must be contravariantly valid on 'I2.operator +(I2)'. 'T2' is covariant. + // abstract static int operator +(I2 x); + Diagnostic(ErrorCode.ERR_UnexpectedVariance, "I2").WithArguments("I2.operator +(I2)", "T2", "covariant", "contravariantly").WithLocation(4, 36), + // (9,36): error CS1961: Invalid variance: The type parameter 'T3' must be contravariantly valid on 'I3.operator +(I3)'. 'T3' is covariant. + // abstract static int operator +(I3 x); + Diagnostic(ErrorCode.ERR_UnexpectedVariance, "I3").WithArguments("I3.operator +(I3)", "T3", "covariant", "contravariantly").WithLocation(9, 36) + ); + } + + [Theory] + [InlineData("+")] + [InlineData("-")] + [InlineData("!")] + [InlineData("~")] + [InlineData("true")] + [InlineData("false")] + public void OperatorSignature_01(string op) + { + var source1 = +@" +interface I1 where T1 : I1 +{ + static bool operator " + op + @"(T1 x) => throw null; +} + +interface I2 where T2 : struct, I2 +{ + static bool operator " + op + @"(T2? x) => throw null; +} + +interface I3 where T3 : I3 +{ + static abstract bool operator " + op + @"(T3 x); +} + +interface I4 where T4 : struct, I4 +{ + static abstract bool operator " + op + @"(T4? x); +} + +class C5 where T5 : C5.I6 +{ + public interface I6 + { + static abstract bool operator " + op + @"(T5 x); + } +} + +interface I7 where T72 : I7 where T71 : T72 +{ + static abstract bool operator " + op + @"(T71 x); +} + +interface I8 where T8 : I9 +{ + static abstract bool operator " + op + @"(T8 x); +} + +interface I9 : I8 where T9 : I9 {} + +interface I10 where T10 : C11 +{ + static abstract bool operator " + op + @"(T10 x); +} + +class C11 : I10 where T11 : C11 {} + +interface I12 +{ + static abstract bool operator " + op + @"(int x); +} + +interface I13 +{ + static abstract bool operator " + op + @"(I13 x); +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + compilation1.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + // (4,26): error CS0562: The parameter of a unary operator must be the containing type + // static bool operator +(T1 x) => throw null; + Diagnostic(ErrorCode.ERR_BadUnaryOperatorSignature, op).WithLocation(4, 26), + // (9,26): error CS0562: The parameter of a unary operator must be the containing type + // static bool operator +(T2? x) => throw null; + Diagnostic(ErrorCode.ERR_BadUnaryOperatorSignature, op).WithLocation(9, 26), + // (26,39): error CS9102: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + // static abstract bool operator +(T5 x); + Diagnostic(ErrorCode.ERR_BadAbstractUnaryOperatorSignature, op).WithLocation(26, 39), + // (32,35): error CS9102: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + // static abstract bool operator +(T71 x); + Diagnostic(ErrorCode.ERR_BadAbstractUnaryOperatorSignature, op).WithLocation(32, 35), + // (37,35): error CS9102: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + // static abstract bool operator +(T8 x); + Diagnostic(ErrorCode.ERR_BadAbstractUnaryOperatorSignature, op).WithLocation(37, 35), + // (44,35): error CS9102: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + // static abstract bool operator +(T10 x); + Diagnostic(ErrorCode.ERR_BadAbstractUnaryOperatorSignature, op).WithLocation(44, 35), + // (51,35): error CS9102: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + // static abstract bool operator false(int x); + Diagnostic(ErrorCode.ERR_BadAbstractUnaryOperatorSignature, op).WithLocation(51, 35) + ); + } + + [Theory] + [InlineData("++")] + [InlineData("--")] + public void OperatorSignature_02(string op) + { + var source1 = +@" +interface I1 where T1 : I1 +{ + static T1 operator " + op + @"(T1 x) => throw null; +} + +interface I2 where T2 : struct, I2 +{ + static T2? operator " + op + @"(T2? x) => throw null; +} + +interface I3 where T3 : I3 +{ + static abstract T3 operator " + op + @"(T3 x); +} + +interface I4 where T4 : struct, I4 +{ + static abstract T4? operator " + op + @"(T4? x); +} + +class C5 where T5 : C5.I6 +{ + public interface I6 + { + static abstract T5 operator " + op + @"(T5 x); + } +} + +interface I7 where T72 : I7 where T71 : T72 +{ + static abstract T71 operator " + op + @"(T71 x); +} + +interface I8 where T8 : I9 +{ + static abstract T8 operator " + op + @"(T8 x); +} + +interface I9 : I8 where T9 : I9 {} + +interface I10 where T10 : C11 +{ + static abstract T10 operator " + op + @"(T10 x); +} + +class C11 : I10 where T11 : C11 {} + +interface I12 +{ + static abstract int operator " + op + @"(int x); +} + +interface I13 +{ + static abstract I13 operator " + op + @"(I13 x); +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + compilation1.VerifyDiagnostics( + // (4,24): error CS0559: The parameter type for ++ or -- operator must be the containing type + // static T1 operator ++(T1 x) => throw null; + Diagnostic(ErrorCode.ERR_BadIncDecSignature, op).WithLocation(4, 24), + // (9,25): error CS0559: The parameter type for ++ or -- operator must be the containing type + // static T2? operator ++(T2? x) => throw null; + Diagnostic(ErrorCode.ERR_BadIncDecSignature, op).WithLocation(9, 25), + // (26,37): error CS9103: The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + // static abstract T5 operator ++(T5 x); + Diagnostic(ErrorCode.ERR_BadAbstractIncDecSignature, op).WithLocation(26, 37), + // (32,34): error CS9103: The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + // static abstract T71 operator ++(T71 x); + Diagnostic(ErrorCode.ERR_BadAbstractIncDecSignature, op).WithLocation(32, 34), + // (37,33): error CS9103: The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + // static abstract T8 operator ++(T8 x); + Diagnostic(ErrorCode.ERR_BadAbstractIncDecSignature, op).WithLocation(37, 33), + // (44,34): error CS9103: The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + // static abstract T10 operator ++(T10 x); + Diagnostic(ErrorCode.ERR_BadAbstractIncDecSignature, op).WithLocation(44, 34), + // (51,34): error CS9103: The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + // static abstract int operator ++(int x); + Diagnostic(ErrorCode.ERR_BadAbstractIncDecSignature, op).WithLocation(51, 34) + ); + } + + [Theory] + [InlineData("++")] + [InlineData("--")] + public void OperatorSignature_03(string op) + { + var source1 = +@" +interface I1 where T1 : I1 +{ + static T1 operator " + op + @"(I1 x) => throw null; +} + +interface I2 where T2 : struct, I2 +{ + static T2? operator " + op + @"(I2 x) => throw null; +} + +interface I3 where T3 : I3 +{ + static abstract T3 operator " + op + @"(I3 x); +} + +interface I4 where T4 : struct, I4 +{ + static abstract T4? operator " + op + @"(I4 x); +} + +class C5 where T5 : C5.I6 +{ + public interface I6 + { + static abstract T5 operator " + op + @"(I6 x); + } +} + +interface I7 where T72 : I7 where T71 : T72 +{ + static abstract T71 operator " + op + @"(I7 x); +} + +interface I8 where T8 : I9 +{ + static abstract T8 operator " + op + @"(I8 x); +} + +interface I9 : I8 where T9 : I9 {} + +interface I10 where T10 : C11 +{ + static abstract T10 operator " + op + @"(I10 x); +} + +class C11 : I10 where T11 : C11 {} + +interface I12 +{ + static abstract int operator " + op + @"(I12 x); +} + +interface I13 where T13 : struct, I13 +{ + static abstract T13? operator " + op + @"(T13 x); +} + +interface I14 where T14 : struct, I14 +{ + static abstract T14 operator " + op + @"(T14? x); +} + +interface I15 where T151 : I15 where T152 : I15 +{ + static abstract T151 operator " + op + @"(T152 x); +} + +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + compilation1.VerifyDiagnostics( + // (4,24): error CS0448: The return type for ++ or -- operator must match the parameter type or be derived from the parameter type + // static T1 operator ++(I1 x) => throw null; + Diagnostic(ErrorCode.ERR_BadIncDecRetType, op).WithLocation(4, 24), + // (9,25): error CS0448: The return type for ++ or -- operator must match the parameter type or be derived from the parameter type + // static T2? operator ++(I2 x) => throw null; + Diagnostic(ErrorCode.ERR_BadIncDecRetType, op).WithLocation(9, 25), + // (19,34): error CS9104: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + // static abstract T4? operator ++(I4 x); + Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(19, 34), + // (26,37): error CS9104: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + // static abstract T5 operator ++(I6 x); + Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(26, 37), + // (32,34): error CS9104: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + // static abstract T71 operator ++(I7 x); + Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(32, 34), + // (37,33): error CS9104: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + // static abstract T8 operator ++(I8 x); + Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(37, 33), + // (44,34): error CS9104: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + // static abstract T10 operator ++(I10 x); + Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(44, 34), + // (51,34): error CS9104: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + // static abstract int operator ++(I12 x); + Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(51, 34), + // (56,35): error CS9104: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + // static abstract T13? operator ++(T13 x); + Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(56, 35), + // (61,34): error CS9104: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + // static abstract T14 operator ++(T14? x); + Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(61, 34), + // (66,35): error CS9104: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + // static abstract T151 operator ++(T152 x); + Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(66, 35) + ); + } + + [Theory] + [InlineData("+")] + [InlineData("-")] + [InlineData("*")] + [InlineData("/")] + [InlineData("%")] + [InlineData("&")] + [InlineData("|")] + [InlineData("^")] + [InlineData("<")] + [InlineData(">")] + [InlineData("<=")] + [InlineData(">=")] + public void OperatorSignature_04(string op) + { + var source1 = +@" +interface I1 where T1 : I1 +{ + static bool operator " + op + @"(T1 x, bool y) => throw null; +} + +interface I2 where T2 : struct, I2 +{ + static bool operator " + op + @"(T2? x, bool y) => throw null; +} + +interface I3 where T3 : I3 +{ + static abstract bool operator " + op + @"(T3 x, bool y); +} + +interface I4 where T4 : struct, I4 +{ + static abstract bool operator " + op + @"(T4? x, bool y); +} + +class C5 where T5 : C5.I6 +{ + public interface I6 + { + static abstract bool operator " + op + @"(T5 x, bool y); + } +} + +interface I7 where T72 : I7 where T71 : T72 +{ + static abstract bool operator " + op + @"(T71 x, bool y); +} + +interface I8 where T8 : I9 +{ + static abstract bool operator " + op + @"(T8 x, bool y); +} + +interface I9 : I8 where T9 : I9 {} + +interface I10 where T10 : C11 +{ + static abstract bool operator " + op + @"(T10 x, bool y); +} + +class C11 : I10 where T11 : C11 {} + +interface I12 +{ + static abstract bool operator " + op + @"(int x, bool y); +} + +interface I13 +{ + static abstract bool operator " + op + @"(I13 x, bool y); +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + compilation1.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + // (4,26): error CS0563: One of the parameters of a binary operator must be the containing type + // static bool operator +(T1 x, bool y) => throw null; + Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, op).WithLocation(4, 26), + // (9,26): error CS0563: One of the parameters of a binary operator must be the containing type + // static bool operator +(T2? x, bool y) => throw null; + Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, op).WithLocation(9, 26), + // (26,39): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // static abstract bool operator +(T5 x, bool y); + Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(26, 39), + // (32,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // static abstract bool operator +(T71 x, bool y); + Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(32, 35), + // (37,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // static abstract bool operator +(T8 x, bool y); + Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(37, 35), + // (44,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // static abstract bool operator +(T10 x, bool y); + Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(44, 35), + // (51,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // static abstract bool operator +(int x, bool y); + Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(51, 35) + ); + } + + [Theory] + [InlineData("+")] + [InlineData("-")] + [InlineData("*")] + [InlineData("/")] + [InlineData("%")] + [InlineData("&")] + [InlineData("|")] + [InlineData("^")] + [InlineData("<")] + [InlineData(">")] + [InlineData("<=")] + [InlineData(">=")] + public void OperatorSignature_05(string op) + { + var source1 = +@" +interface I1 where T1 : I1 +{ + static bool operator " + op + @"(bool y, T1 x) => throw null; +} + +interface I2 where T2 : struct, I2 +{ + static bool operator " + op + @"(bool y, T2? x) => throw null; +} + +interface I3 where T3 : I3 +{ + static abstract bool operator " + op + @"(bool y, T3 x); +} + +interface I4 where T4 : struct, I4 +{ + static abstract bool operator " + op + @"(bool y, T4? x); +} + +class C5 where T5 : C5.I6 +{ + public interface I6 + { + static abstract bool operator " + op + @"(bool y, T5 x); + } +} + +interface I7 where T72 : I7 where T71 : T72 +{ + static abstract bool operator " + op + @"(bool y, T71 x); +} + +interface I8 where T8 : I9 +{ + static abstract bool operator " + op + @"(bool y, T8 x); +} + +interface I9 : I8 where T9 : I9 {} + +interface I10 where T10 : C11 +{ + static abstract bool operator " + op + @"(bool y, T10 x); +} + +class C11 : I10 where T11 : C11 {} + +interface I12 +{ + static abstract bool operator " + op + @"(bool y, int x); +} + +interface I13 +{ + static abstract bool operator " + op + @"(bool y, I13 x); +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + compilation1.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + // (4,26): error CS0563: One of the parameters of a binary operator must be the containing type + // static bool operator +(bool y, T1 x) => throw null; + Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, op).WithLocation(4, 26), + // (9,26): error CS0563: One of the parameters of a binary operator must be the containing type + // static bool operator +(bool y, T2? x) => throw null; + Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, op).WithLocation(9, 26), + // (26,39): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // static abstract bool operator +(bool y, T5 x); + Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(26, 39), + // (32,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // static abstract bool operator +(bool y, T71 x); + Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(32, 35), + // (37,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // static abstract bool operator +(bool y, T8 x); + Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(37, 35), + // (44,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // static abstract bool operator +(bool y, T10 x); + Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(44, 35), + // (51,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // static abstract bool operator +(bool y, int x); + Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(51, 35) + ); + } + + [Theory] + [InlineData("<<")] + [InlineData(">>")] + public void OperatorSignature_06(string op) + { + var source1 = +@" +interface I1 where T1 : I1 +{ + static bool operator " + op + @"(T1 x, int y) => throw null; +} + +interface I2 where T2 : struct, I2 +{ + static bool operator " + op + @"(T2? x, int y) => throw null; +} + +interface I3 where T3 : I3 +{ + static abstract bool operator " + op + @"(T3 x, int y); +} + +interface I4 where T4 : struct, I4 +{ + static abstract bool operator " + op + @"(T4? x, int y); +} + +class C5 where T5 : C5.I6 +{ + public interface I6 + { + static abstract bool operator " + op + @"(T5 x, int y); + } +} + +interface I7 where T72 : I7 where T71 : T72 +{ + static abstract bool operator " + op + @"(T71 x, int y); +} + +interface I8 where T8 : I9 +{ + static abstract bool operator " + op + @"(T8 x, int y); +} + +interface I9 : I8 where T9 : I9 {} + +interface I10 where T10 : C11 +{ + static abstract bool operator " + op + @"(T10 x, int y); +} + +class C11 : I10 where T11 : C11 {} + +interface I12 +{ + static abstract bool operator " + op + @"(int x, int y); +} + +interface I13 +{ + static abstract bool operator " + op + @"(I13 x, int y); +} + +interface I14 +{ + static abstract bool operator " + op + @"(I14 x, bool y); +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + compilation1.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + // (4,26): error CS0564: The first operand of an overloaded shift operator must have the same type as the containing type, and the type of the second operand must be int + // static bool operator <<(T1 x, int y) => throw null; + Diagnostic(ErrorCode.ERR_BadShiftOperatorSignature, op).WithLocation(4, 26), + // (9,26): error CS0564: The first operand of an overloaded shift operator must have the same type as the containing type, and the type of the second operand must be int + // static bool operator <<(T2? x, int y) => throw null; + Diagnostic(ErrorCode.ERR_BadShiftOperatorSignature, op).WithLocation(9, 26), + // (26,39): error CS9106: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + // static abstract bool operator <<(T5 x, int y); + Diagnostic(ErrorCode.ERR_BadAbstractShiftOperatorSignature, op).WithLocation(26, 39), + // (32,35): error CS9106: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + // static abstract bool operator <<(T71 x, int y); + Diagnostic(ErrorCode.ERR_BadAbstractShiftOperatorSignature, op).WithLocation(32, 35), + // (37,35): error CS9106: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + // static abstract bool operator <<(T8 x, int y); + Diagnostic(ErrorCode.ERR_BadAbstractShiftOperatorSignature, op).WithLocation(37, 35), + // (44,35): error CS9106: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + // static abstract bool operator <<(T10 x, int y); + Diagnostic(ErrorCode.ERR_BadAbstractShiftOperatorSignature, op).WithLocation(44, 35), + // (51,35): error CS9106: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + // static abstract bool operator <<(int x, int y); + Diagnostic(ErrorCode.ERR_BadAbstractShiftOperatorSignature, op).WithLocation(51, 35), + // (61,35): error CS9106: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + // static abstract bool operator <<(I14 x, bool y); + Diagnostic(ErrorCode.ERR_BadAbstractShiftOperatorSignature, op).WithLocation(61, 35) + ); + } } } From 0d08f9418afff745dd1f3903a7a1580468a1fb3e Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Fri, 2 Apr 2021 17:48:40 -0700 Subject: [PATCH 032/127] PR feedback --- ...eTrackingService.FindReferencesProgress.cs | 9 ++ .../ValueTrackingService.Visitor.cs | 144 +++++++++++------- .../ValueTracking/ValueTrackingService.cs | 14 -- .../ValueTracking/CSharpValueTrackingTests.cs | 84 +++++++++- .../VisualBasicValueTrackingTests.cs | 10 +- 5 files changed, 183 insertions(+), 78 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs index 0b83d311d46bb..2fc7fe9360b8e 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs @@ -63,6 +63,15 @@ public async ValueTask OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation l var syntaxFacts = location.Document.GetRequiredLanguageService(); var node = location.Location.FindNode(CancellationToken.None); + // Assignments to a member using a "this." or "Me." result in the node being an + // identifier and the parent of the node being the member access expression. The member + // access expression gives the right value for "IsLeftSideOfAnyAssignment" but also + // gives the correct operation, where as the IdentifierSyntax does not. + if (node.Parent is not null && syntaxFacts.IsAnyMemberAccessExpression(node.Parent)) + { + node = node.Parent; + } + if (syntaxFacts.IsLeftSideOfAnyAssignment(node)) { await AddItemsFromAssignmentAsync(location.Document, node, _operationCollector, _cancellationToken).ConfigureAwait(false); diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs index 762117d4871da..f2a47c2c45d82 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs @@ -38,26 +38,23 @@ ILocalReferenceOperation or IParameterReferenceOperation or IFieldReferenceOperation or IPropertyReferenceOperation => VisitReferenceAsync(operation, cancellationToken), + IAssignmentOperation assignmentOperation => VisitAssignmentOperationAsync(assignmentOperation, cancellationToken), + // All the operations in this group don't have meaningful use themselves, but the child operations + // could be meaningful for value tracking. + // Ex: Binary operation of [| x + y |] + // both x and y should be evaluated separately + IBinaryOperation or + IAwaitOperation or + IBlockOperation => VisitChildrenAsync(operation, cancellationToken), + // Default to reporting if there is symbol information available _ => VisitDefaultAsync(operation, cancellationToken) }; private async Task VisitDefaultAsync(IOperation operation, CancellationToken cancellationToken) { - // If the operation has children, always visit the children instead of the root - // operation. They are the interesting bits for ValueTracking - if (operation.Children.Any()) - { - foreach (var childOperation in operation.Children) - { - await VisitAsync(childOperation, cancellationToken).ConfigureAwait(false); - } - - return; - } - var semanticModel = operation.SemanticModel; if (semanticModel is null) { @@ -73,8 +70,16 @@ private async Task VisitDefaultAsync(IOperation operation, CancellationToken can await AddOperationAsync(operation, symbolInfo.Symbol, cancellationToken).ConfigureAwait(false); } + private async Task VisitChildrenAsync(IOperation operation, CancellationToken cancellationToken) + { + foreach (var child in operation.Children) + { + await VisitAsync(child, cancellationToken).ConfigureAwait(false); + } + } + private Task VisitAssignmentOperationAsync(IAssignmentOperation assignmentOperation, CancellationToken cancellationToken) - => VisitDefaultAsync(assignmentOperation.Value, cancellationToken); + => VisitAsync(assignmentOperation.Value, cancellationToken); private Task VisitObjectCreationAsync(IObjectCreationOperation objectCreationOperation, CancellationToken cancellationToken) => TrackArgumentsAsync(objectCreationOperation.Arguments, cancellationToken); @@ -93,7 +98,7 @@ IParameterReferenceOperation or IFieldReferenceOperation or IPropertyReferenceOperation); - if (IsArgument(operation, out var argumentOperation) && argumentOperation.Parameter is not null) + if (IsContainedIn(operation, out var argumentOperation) && argumentOperation.Parameter is not null) { if (argumentOperation.Parameter.IsRefOrOut()) { @@ -107,10 +112,10 @@ IFieldReferenceOperation or return AddReference(operation, cancellationToken); } - if (IsReturn(operation)) + if (IsContainedIn(operation) || IsContainedIn(operation)) { - // If the reference is part of a return operation we want to track where the values come from - // since they contribute to the "output" of the method and are relavent for value tracking. + // If the reference is part of a return operation or assignment operation we want to track where the values come from + // since they contribute to the "output" of the method/assignment and are relavent for value tracking. return AddReference(operation, cancellationToken); } @@ -127,56 +132,24 @@ Task AddReference(IOperation operation, CancellationToken cancellationToken) }; } - private static bool IsArgument(IOperation? operation, [NotNullWhen(returnValue: true)] out IArgumentOperation? argumentOperation) - { - while (operation is not null) - { - if (operation is IArgumentOperation tmpArgumentOperation) - { - argumentOperation = tmpArgumentOperation; - return true; - } - - operation = operation.Parent; - } - - argumentOperation = null; - return false; - } - - private static bool IsReturn(IOperation? operation) - { - while (operation is not null) - { - if (operation is IReturnOperation) - { - return true; - } - - operation = operation.Parent; - } - - return false; - } - - private async Task VisitLiteralAsync(ILiteralOperation literalOperation, CancellationToken cancellationToken) + private Task VisitLiteralAsync(ILiteralOperation literalOperation, CancellationToken cancellationToken) { if (literalOperation.Type is null) { - return; + return Task.CompletedTask; } - await AddOperationAsync(literalOperation, literalOperation.Type, cancellationToken).ConfigureAwait(false); + return AddOperationAsync(literalOperation, literalOperation.Type, cancellationToken); } - private async Task VisitReturnAsync(IReturnOperation returnOperation, CancellationToken cancellationToken) + private Task VisitReturnAsync(IReturnOperation returnOperation, CancellationToken cancellationToken) { if (returnOperation.ReturnedValue is null) { - return; + return Task.CompletedTask; } - await VisitAsync(returnOperation.ReturnedValue, cancellationToken).ConfigureAwait(false); + return VisitAsync(returnOperation.ReturnedValue, cancellationToken); } private async Task AddOperationAsync(IOperation operation, ISymbol symbol, CancellationToken cancellationToken) @@ -192,11 +165,13 @@ private async Task TrackArgumentsAsync(ImmutableArray argume { var collectorsAndArgumentMap = argumentOperations .Where(ShouldTrackArgument) + // Clone the collector here to allow each argument to report multiple items. + // See Clone() docs for more details .Select(argument => (collector: Clone(), argument)) .ToImmutableArray(); var tasks = collectorsAndArgumentMap - .Select(pair => pair.collector.VisitAsync(pair.argument, cancellationToken)); + .Select(pair => Task.Run(() => pair.collector.VisitAsync(pair.argument, cancellationToken))); await Task.WhenAll(tasks).ConfigureAwait(false); @@ -211,6 +186,19 @@ private async Task TrackArgumentsAsync(ImmutableArray argume } } + /// + /// Clone the current collector into a new one with + /// the same parent but a separate progress collector. + /// This allows collection of items given the same state + /// as this collector while also keeping them "grouped" separately. + /// + /// + /// This is useful for cases such as tracking arguments, where each + /// argument may be an expression or something else. We want to track each + /// argument expression in the correct order, but a single argument may produce + /// multiple items. By cloning we can track the items for each argument and then + /// gather them all at the end to report in the correct order. + /// private OperationCollector Clone() { var collector = new ValueTrackingProgressCollector(); @@ -220,13 +208,57 @@ private OperationCollector Clone() private static bool ShouldTrackArgument(IArgumentOperation argumentOperation) { + // Ref or Out arguments always contribute data as "assignments" + // across method calls return argumentOperation.Parameter?.IsRefOrOut() == true + + // If the argument value is an expression, binary operation, or + // invocation then parts of the operation need to be evaluated + // to see if they contribute data for value tracking || argumentOperation.Value is IExpressionStatementOperation or IBinaryOperation or IInvocationOperation + + // If the argument value is a parameter reference, then the method calls + // leading to that parameter value should be tracked as well. + // Ex: + // string Prepend(string s1) => "pre" + s1; + // string CallPrepend(string [|s2|]) => Prepend(s2); + // Tracking [|s2|] into calls as an argument means that we + // need to know where [|s2|] comes from and how it contributes + // to the value s1 or IParameterReferenceOperation + + // A literal value as an argument is a dead end for data, but still contributes + // to a value and should be shown in value tracking. It should never expand + // further though. + // Ex: + // string Prepend(string [|s|]) => "pre" + s; + // string DefaultPrepend() => Prepend("default"); + // [|s|] is the parameter we need to track values for, which + // is assigned to "default" in DefaultPrepend or ILiteralOperation; } + + private static bool IsContainedIn(IOperation? operation) where TContainingOperation : IOperation + => IsContainedIn(operation, out var _); + + private static bool IsContainedIn(IOperation? operation, [NotNullWhen(returnValue: true)] out TContainingOperation? containingOperation) where TContainingOperation : IOperation + { + while (operation is not null) + { + if (operation is TContainingOperation) + { + containingOperation = (TContainingOperation)operation; + return true; + } + + operation = operation.Parent; + } + + containingOperation = default; + return false; + } } } } diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index c12f3ec1f0280..fb5c8ba494bae 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -89,20 +89,6 @@ or ILocalSymbol await progressCollector.TryReportAsync(document.Project.Solution, node.GetLocation(), symbol, cancellationToken).ConfigureAwait(false); } } - else if (node is not null && symbol is null) - { - // If the correct symbol couldn't be determined but a node is available, try to let - // the operation collector handle the operation and report as needed - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var operation = semanticModel.GetOperation(node, cancellationToken); - - if (operation is null) - { - return; - } - - await operationCollector.VisitAsync(operation, cancellationToken).ConfigureAwait(false); - } } public async Task> TrackValueSourceAsync( diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs index 69231e895ef15..b830c10f4d3c0 100644 --- a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -7,7 +7,6 @@ using Xunit; using Microsoft.CodeAnalysis.Test.Utilities; using System.Linq; -using System; namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking { @@ -47,6 +46,39 @@ await ValidateItemsAsync( }); } + [Fact] + public async Task TestPropertyWithThis() + { + var code = +@" +class C +{ + public string $$S { get; set; } = """"""; + + public void SetS(string s) + { + this.S = s; + } + + public string GetS() => this.S; +} +"; + using var workspace = TestWorkspace.CreateCSharp(code); + + // + // property S + // |> S = s [Code.cs:7] + // |> public string S { get; set; } [Code.cs:3] + // + await ValidateItemsAsync( + workspace, + itemInfo: new[] + { + (7, "s"), + (3, "public string S { get; set; } = \"\""), + }); + } + [Fact] public async Task TestField() { @@ -71,9 +103,46 @@ public void SetS(string s) // |> _s = s [Code.cs:7] // |> string _s = "" [Code.cs:3] // - Assert.Equal(2, initialItems.Length); - ValidateItem(initialItems[0], 7); - ValidateItem(initialItems[1], 3); + await ValidateItemsAsync( + workspace, + itemInfo: new[] + { + (7, "s"), + (3, "_s = \"\"") + }); + } + + [Fact] + public async Task TestFieldWithThis() + { + var code = +@" +class C +{ + private string $$_s = """"""; + + public void SetS(string s) + { + this._s = s; + } + + public string GetS() => this._s; +}"; + using var workspace = TestWorkspace.CreateCSharp(code); + var initialItems = await GetTrackedItemsAsync(workspace); + + // + // field _s + // |> this._s = s [Code.cs:7] + // |> string _s = "" [Code.cs:3] + // + await ValidateItemsAsync( + workspace, + itemInfo: new[] + { + (7, "s"), + (3, "_s = \"\"") + }); } [Fact] @@ -587,12 +656,17 @@ void M() initialItems.Single(), childInfo: new[] { + (24, "2"), // |> i = [|2|] [Code.cs:24] (18, "i"), // |> if (TryConvertInt(o, out [|i|])) [Code.cs:18] }); + // |> i = [|2|] [Code.cs:24] + await ValidateChildrenEmptyAsync(workspace, children[0]); + + // |> if (TryConvertInt(o, out [|i|])) [Code.cs:18] children = await ValidateChildrenAsync( workspace, - children.Single(), + children[1], childInfo: new[] { (5, "i") // |> if (int.TryParse(o.ToString(), out [|i|])) [Code.cs:5] diff --git a/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs index ed1536f33d809..b6e5e35d115a5 100644 --- a/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs @@ -78,9 +78,13 @@ End Class // |> Me._s = s [Code.vb:4] // |> Private _s As String = "" [Code.vb:2] // - Assert.Equal(2, initialItems.Length); - ValidateItem(initialItems[0], 5); - ValidateItem(initialItems[1], 2); + await ValidateItemsAsync( + workspace, + itemInfo: new[] + { + (5, "s"), + (2, "_s") + }); } [Fact] From 1cabca710f48990781fd3874cee1ea4ef721ee15 Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Fri, 2 Apr 2021 23:05:37 -0700 Subject: [PATCH 033/127] Fix formatting and messages --- .../ValueTrackingService.Visitor.cs | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs index f2a47c2c45d82..eb5f0d2ff3fbb 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs @@ -201,8 +201,10 @@ private async Task TrackArgumentsAsync(ImmutableArray argume /// private OperationCollector Clone() { - var collector = new ValueTrackingProgressCollector(); - collector.Parent = ProgressCollector.Parent; + var collector = new ValueTrackingProgressCollector + { + Parent = ProgressCollector.Parent + }; return new OperationCollector(collector, Solution); } @@ -219,24 +221,24 @@ private static bool ShouldTrackArgument(IArgumentOperation argumentOperation) or IBinaryOperation or IInvocationOperation - // If the argument value is a parameter reference, then the method calls - // leading to that parameter value should be tracked as well. - // Ex: - // string Prepend(string s1) => "pre" + s1; - // string CallPrepend(string [|s2|]) => Prepend(s2); - // Tracking [|s2|] into calls as an argument means that we - // need to know where [|s2|] comes from and how it contributes - // to the value s1 + // If the argument value is a parameter reference, then the method calls + // leading to that parameter value should be tracked as well. + // Ex: + // string Prepend(string s1) => "pre" + s1; + // string CallPrepend(string [|s2|]) => Prepend(s2); + // Tracking [|s2|] into calls as an argument means that we + // need to know where [|s2|] comes from and how it contributes + // to the value s1 or IParameterReferenceOperation - // A literal value as an argument is a dead end for data, but still contributes - // to a value and should be shown in value tracking. It should never expand - // further though. - // Ex: - // string Prepend(string [|s|]) => "pre" + s; - // string DefaultPrepend() => Prepend("default"); - // [|s|] is the parameter we need to track values for, which - // is assigned to "default" in DefaultPrepend + // A literal value as an argument is a dead end for data, but still contributes + // to a value and should be shown in value tracking. It should never expand + // further though. + // Ex: + // string Prepend(string [|s|]) => "pre" + s; + // string DefaultPrepend() => Prepend("default"); + // [|s|] is the parameter we need to track values for, which + // is assigned to "default" in DefaultPrepend or ILiteralOperation; } @@ -247,9 +249,9 @@ private static bool IsContainedIn(IOperation? operation, [ { while (operation is not null) { - if (operation is TContainingOperation) + if (operation is TContainingOperation tmpOperation) { - containingOperation = (TContainingOperation)operation; + containingOperation = tmpOperation; return true; } From 993bae4d4aa023a975d486792e953c789f54320b Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Tue, 6 Apr 2021 11:43:17 -0700 Subject: [PATCH 034/127] Update commenting on ShouldTrackArgument --- .../ValueTrackingService.Visitor.cs | 66 +++++++++++-------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs index eb5f0d2ff3fbb..b83e3f6722aec 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs @@ -212,34 +212,48 @@ private static bool ShouldTrackArgument(IArgumentOperation argumentOperation) { // Ref or Out arguments always contribute data as "assignments" // across method calls - return argumentOperation.Parameter?.IsRefOrOut() == true + if (argumentOperation.Parameter?.IsRefOrOut() == true) + { + return true; + } - // If the argument value is an expression, binary operation, or - // invocation then parts of the operation need to be evaluated - // to see if they contribute data for value tracking - || argumentOperation.Value is IExpressionStatementOperation + // If the argument value is an expression, binary operation, or + // invocation then parts of the operation need to be evaluated + // to see if they contribute data for value tracking + if (argumentOperation.Value is IExpressionStatementOperation or IBinaryOperation - or IInvocationOperation - - // If the argument value is a parameter reference, then the method calls - // leading to that parameter value should be tracked as well. - // Ex: - // string Prepend(string s1) => "pre" + s1; - // string CallPrepend(string [|s2|]) => Prepend(s2); - // Tracking [|s2|] into calls as an argument means that we - // need to know where [|s2|] comes from and how it contributes - // to the value s1 - or IParameterReferenceOperation - - // A literal value as an argument is a dead end for data, but still contributes - // to a value and should be shown in value tracking. It should never expand - // further though. - // Ex: - // string Prepend(string [|s|]) => "pre" + s; - // string DefaultPrepend() => Prepend("default"); - // [|s|] is the parameter we need to track values for, which - // is assigned to "default" in DefaultPrepend - or ILiteralOperation; + or IInvocationOperation) + { + return true; + } + + // If the argument value is a parameter reference, then the method calls + // leading to that parameter value should be tracked as well. + // Ex: + // string Prepend(string s1) => "pre" + s1; + // string CallPrepend(string [|s2|]) => Prepend(s2); + // Tracking [|s2|] into calls as an argument means that we + // need to know where [|s2|] comes from and how it contributes + // to the value s1 + if (argumentOperation.Value is IParameterReferenceOperation) + { + return true; + } + + // A literal value as an argument is a dead end for data, but still contributes + // to a value and should be shown in value tracking. It should never expand + // further though. + // Ex: + // string Prepend(string [|s|]) => "pre" + s; + // string DefaultPrepend() => Prepend("default"); + // [|s|] is the parameter we need to track values for, which + // is assigned to "default" in DefaultPrepend + if (argumentOperation.Value is ILiteralOperation) + { + return true; + } + + return false; } private static bool IsContainedIn(IOperation? operation) where TContainingOperation : IOperation From 622ad1abc62f7ae21af59ba757e5aa68b30fd884 Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Tue, 6 Apr 2021 12:08:35 -0700 Subject: [PATCH 035/127] Move VisitChildrenAsync to VisitDefaultAsync to avoid an allowlist style visit of children --- .../ValueTrackingService.Visitor.cs | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs index b83e3f6722aec..3685899ffa88c 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs @@ -41,20 +41,28 @@ IFieldReferenceOperation or IAssignmentOperation assignmentOperation => VisitAssignmentOperationAsync(assignmentOperation, cancellationToken), - // All the operations in this group don't have meaningful use themselves, but the child operations - // could be meaningful for value tracking. - // Ex: Binary operation of [| x + y |] - // both x and y should be evaluated separately - IBinaryOperation or - IAwaitOperation or - IBlockOperation => VisitChildrenAsync(operation, cancellationToken), - // Default to reporting if there is symbol information available _ => VisitDefaultAsync(operation, cancellationToken) }; private async Task VisitDefaultAsync(IOperation operation, CancellationToken cancellationToken) { + // If an operation has children, desend in them by default. + // For cases that should not be descendend into, they should be explicitly handled + // in VisitAsync. + // Ex: Binary operation of [| x + y |] + // both x and y should be evaluated separately + var childrenVisited = await TryVisitChildrenAsync(operation, cancellationToken).ConfigureAwait(false); + + // In cases where the child operations were visited, they would be added instead of the parent + // currently being evaluated. Do not add the parent as well since it would be redundent. + // Ex: Binary operation of [| x + y |] + // both x and y should be evaluated separately, but the whole operation should not be reported + if (childrenVisited) + { + return; + } + var semanticModel = operation.SemanticModel; if (semanticModel is null) { @@ -70,12 +78,14 @@ private async Task VisitDefaultAsync(IOperation operation, CancellationToken can await AddOperationAsync(operation, symbolInfo.Symbol, cancellationToken).ConfigureAwait(false); } - private async Task VisitChildrenAsync(IOperation operation, CancellationToken cancellationToken) + private async Task TryVisitChildrenAsync(IOperation operation, CancellationToken cancellationToken) { foreach (var child in operation.Children) { await VisitAsync(child, cancellationToken).ConfigureAwait(false); } + + return operation.Children.Any(); } private Task VisitAssignmentOperationAsync(IAssignmentOperation assignmentOperation, CancellationToken cancellationToken) From 4797c229a52ecc7ecaf076881175babedb253d96 Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Tue, 6 Apr 2021 12:13:25 -0700 Subject: [PATCH 036/127] Use Equals instead of == when comparing symbols --- src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index fb5c8ba494bae..9f60db4a498f1 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -131,7 +131,7 @@ public async Task TrackValueSourceAsync( // In this case, s is being tracked because it contributed to the return of the method. We only // want to track assignments to s that could impact the return rather than tracking the same method // twice. - var isParameterForPreviousTrackedMethod = previousSymbol == parameterSymbol.ContainingSymbol; + var isParameterForPreviousTrackedMethod = previousSymbol?.Equals(parameterSymbol.ContainingSymbol) == true; // For Ref or Out parameters, they contribute data across method calls through assignments // within the method. No need to track returns From 6de0d321878313b937c639a714b50246ff3fd3af Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Tue, 6 Apr 2021 15:39:47 -0700 Subject: [PATCH 037/127] Update UI to navigate to items on selection --- .../ValueTrackedTreeItemViewModel.cs | 37 +++++++++++++++---- .../ValueTrackingCommandHandler.cs | 4 +- .../ValueTracking/ValueTrackingToolWindow.cs | 17 +++++---- .../Def/ValueTracking/ValueTrackingTree.xaml | 2 +- .../ValueTracking/ValueTrackingTree.xaml.cs | 6 +++ .../ValueTrackingTreeItemViewModel.cs | 34 +++++++++++++++-- .../ValueTrackingTreeViewModel.cs | 9 ----- 7 files changed, 81 insertions(+), 28 deletions(-) diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs index 1ad5ea162fc39..f9a9d89981263 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs @@ -8,12 +8,14 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.ValueTracking; -using Roslyn.Utilities; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.VisualStudio.Text.Classification; -using Microsoft.VisualStudio.Shell; +using Microsoft.CodeAnalysis.Navigation; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.ValueTracking; using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Text.Classification; +using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.ValueTracking { @@ -34,16 +36,19 @@ public ValueTrackedTreeItemViewModel( ClassificationTypeMap classificationTypeMap, IGlyphService glyphService, IValueTrackingService valueTrackingService, + IThreadingContext threadingContext, ImmutableArray children = default) : base( trackedItem.Document, - trackedItem.LineSpan.Start, + trackedItem.LineSpan, trackedItem.SourceText, trackedItem.Symbol, trackedItem.ClassifiedSpans, classificationFormatMap, classificationTypeMap, - glyphService) + glyphService, + threadingContext, + children: children) { _trackedItem = trackedItem; @@ -107,6 +112,23 @@ private void CalculateChildren() TaskScheduler.FromCurrentSynchronizationContext()); } + public override void Select() + { + var workspace = Document.Project.Solution.Workspace; + var navigationService = workspace.Services.GetService(); + if (navigationService is null) + { + return; + } + + // While navigating do not activate the tab, which will change focus from the tool window + var options = workspace.Options + .WithChangedOption(new OptionKey(NavigationOptions.PreferProvisionalTab), true) + .WithChangedOption(new OptionKey(NavigationOptions.ActivateTab), false); + + navigationService.TryNavigateToSpan(workspace, Document.Id, _trackedItem.Span, options, ThreadingContext.DisposalToken); + } + private async Task> CalculateChildrenAsync(CancellationToken cancellationToken) { var valueTrackedItems = await _valueTrackingService.TrackValueSourceAsync( @@ -123,7 +145,8 @@ private async Task> CalculateChil _classificationFormatMap, _classificationTypeMap, _glyphService, - _valueTrackingService)); + _valueTrackingService, + ThreadingContext)); } return builder.ToImmutableArray(); diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs index 1312e3e2095d5..3e9df448573a3 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs @@ -163,13 +163,14 @@ private async Task ShowToolWindowAsync(ITextView textView, ISymbol selectedSymbo var root = new ValueTrackingTreeItemViewModel( document, - lineSpan.Start, + lineSpan, sourceText, selectedSymbol, classificationResult.ClassifiedSpans, classificationFormatMap, _typeMap, _glyphService, + _threadingContext, childViewModels); await ShowToolWindowAsync(root, cancellationToken).ConfigureAwait(false); @@ -182,6 +183,7 @@ ValueTrackingTreeItemViewModel CreateViewModel(ValueTrackedItem valueTrackedItem _typeMap, _glyphService, valueTrackingService, + _threadingContext, children); } diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs index bfbad0894d99a..9f50d41b8a05d 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs @@ -15,17 +15,20 @@ internal class ValueTrackingToolWindow : ToolWindowPane public static ValueTrackingToolWindow? Instance { get; set; } private readonly ValueTrackingTreeViewModel _viewModel; + public ValueTrackingToolWindow() : base(null) + { + this.Caption = "Value Tracking"; + + _viewModel = new(); + Content = new ValueTrackingTree(_viewModel); + } + public ValueTrackingToolWindow(ValueTrackingTreeItemViewModel root) : base(null) { - if (Instance is not null) - { - throw new Exception("Cannot initialize the window more than once"); - } - - this.Caption = "Value Tracking"; + Caption = "Value Tracking"; - _viewModel = new ValueTrackingTreeViewModel(root); + _viewModel = new(root); Content = new ValueTrackingTree(_viewModel); } diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml index c84201404c116..7051102bf957a 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml @@ -8,7 +8,7 @@ mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> - + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs index 28ecb5835f3bd..d9a7e73fd46e6 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs @@ -34,8 +34,7 @@ public ValueTrackingTree(ValueTrackingTreeViewModel viewModel) private void ValueTrackingTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs e) { - var selectedTreeViewItem = (ValueTrackingTreeItemViewModel)ValueTrackingTreeView.SelectedItem; - selectedTreeViewItem.Select(); + _viewModel.SelectedItem = (ValueTrackingTreeItemViewModel)e.NewValue; } } } diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs index c524603c8ec59..d5a39de1d9a93 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs @@ -32,10 +32,10 @@ internal class ValueTrackingTreeItemViewModel : TreeViewItemBase protected Document Document { get; } protected IThreadingContext ThreadingContext { get; } - public int LineNumber => LineSpan.Start; + public int LineNumber => LineSpan.Start + 1; // LineSpan is 0 indexed, editors are not public ObservableCollection ChildItems { get; } = new(); - public string FileDisplay => $"[{Document.Name}:{LineNumber}]"; + public string FileName => Document.FilePath ?? Document.Name; public ImageSource GlyphImage => _symbol.GetGlyph().GetImageSource(_glyphService); diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs index 544d5448aa9a0..f7932d4e04b9e 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs @@ -15,6 +15,43 @@ namespace Microsoft.VisualStudio.LanguageServices.ValueTracking { internal class ValueTrackingTreeViewModel : INotifyPropertyChanged { + private Brush? _highlightBrush; + public Brush? HighlightBrush + { + get => _highlightBrush; + set => SetProperty(ref _highlightBrush, value); + } + + public IClassificationFormatMap ClassificationFormatMap { get; } + public ClassificationTypeMap ClassificationTypeMap { get; } + public IEditorFormatMapService FormatMapService { get; } + public ObservableCollection Roots { get; } = new(); + + private ValueTrackingTreeItemViewModel? _selectedItem; + public ValueTrackingTreeItemViewModel? SelectedItem + { + get => _selectedItem; + set => SetProperty(ref _selectedItem, value); + } + + private string _selectedItemFile = ""; + public string SelectedItemFile + { + get => _selectedItemFile; + set => SetProperty(ref _selectedItemFile, value); + } + + private int _selectedItemLine; + public int SelectedItemLine + { + get => _selectedItemLine; + set => SetProperty(ref _selectedItemLine, value); + } + + public bool ShowDetails => SelectedItem is not null; + + public event PropertyChangedEventHandler? PropertyChanged; + public ValueTrackingTreeViewModel(IClassificationFormatMap classificationFormatMap, ClassificationTypeMap classificationTypeMap, IEditorFormatMapService _formatMapService) { ClassificationFormatMap = classificationFormatMap; @@ -25,21 +62,21 @@ public ValueTrackingTreeViewModel(IClassificationFormatMap classificationFormatM .GetProperties(ReferenceHighlightTag.TagId); HighlightBrush = properties["Background"] as Brush; - } - private Brush? _highlightBrush; - public Brush? HighlightBrush - { - get => _highlightBrush; - set => SetProperty(ref _highlightBrush, value); + PropertyChanged += Self_PropertyChanged; } - public IClassificationFormatMap ClassificationFormatMap { get; } - public ClassificationTypeMap ClassificationTypeMap { get; } - public IEditorFormatMapService FormatMapService { get; } - public ObservableCollection Roots { get; } = new(); + private void Self_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(SelectedItem)) + { + SelectedItemFile = SelectedItem?.FileName ?? ""; + SelectedItemLine = SelectedItem?.LineNumber ?? 0; + NotifyPropertyChanged(nameof(ShowDetails)); - public event PropertyChangedEventHandler? PropertyChanged; + SelectedItem?.Select(); + } + } private void SetProperty(ref T field, T value, [CallerMemberName] string name = "") { From 174ccd868ebffcd971dbcdc12bb012cba7f74a6a Mon Sep 17 00:00:00 2001 From: "Andrew Hall (METAL)" Date: Tue, 13 Apr 2021 14:39:18 -0700 Subject: [PATCH 047/127] Handle parameterless initialization of tool window --- .../ValueTrackingCommandHandler.cs | 7 +++ .../ValueTracking/ValueTrackingToolWindow.cs | 43 ++++++++++++++++--- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs index e067d48bcad96..af6e4c97a20f8 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs @@ -230,6 +230,13 @@ await roslynPackage.ShowToolWindowAsync( roslynPackage.DisposalToken).ConfigureAwait(false); } + // This can happen if the tool window was initialized outside of this command handler. The ViewModel + // still needs to be initialized but had no necessary context. Provide that context now in the command handler. + if (ValueTrackingToolWindow.Instance.ViewModel is null) + { + ValueTrackingToolWindow.Instance.ViewModel = new ValueTrackingTreeViewModel(_classificationFormatMapService.GetClassificationFormatMap(textView), _typeMap, _formatMapService); + } + return ValueTrackingToolWindow.Instance; } diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs index ed7b4e394d301..effe48db8d378 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs @@ -4,33 +4,60 @@ using System; using System.Linq; +using System.Windows.Controls; using System.Runtime.InteropServices; using Microsoft.VisualStudio.Shell; +using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.ValueTracking { [Guid(Guids.ValueTrackingToolWindowIdString)] internal class ValueTrackingToolWindow : ToolWindowPane { + private Grid _rootGrid = new(); + public static ValueTrackingToolWindow? Instance { get; set; } - public ValueTrackingTreeViewModel? ViewModel { get; } + private ValueTrackingTreeViewModel? _viewModel; + public ValueTrackingTreeViewModel? ViewModel + { + get => _viewModel; + set + { + if (value is null) + { + throw new ArgumentNullException(nameof(ViewModel)); + } + + _viewModel = value; + _rootGrid.Children.Clear(); + _rootGrid.Children.Add(new ValueTrackingTree(_viewModel)); + } + } + + /// + /// This paramterless constructor is used when + /// the tool window is initialized on open without any + /// context. If the tool window is left open across shutdown/restart + /// of VS for example, then this gets called. + /// public ValueTrackingToolWindow() : base(null) { Caption = ServicesVSResources.Value_Tracking; - Content = new BindableTextBlock() + + _rootGrid.Children.Add(new TextBlock() { Text = "Select an appropriate symbol to start value tracking" - }; + }); + + Content = _rootGrid; } public ValueTrackingToolWindow(ValueTrackingTreeViewModel viewModel) : base(null) { - Caption = ServicesVSResources.Value_Tracking; - + Content = _rootGrid; ViewModel = viewModel; - Content = new ValueTrackingTree(ViewModel); } public ValueTrackingTreeItemViewModel? Root @@ -38,11 +65,13 @@ public ValueTrackingTreeItemViewModel? Root get => ViewModel?.Roots.Single(); set { - if (ViewModel is null || value is null) + if (value is null) { return; } + Contract.ThrowIfNull(ViewModel); + ViewModel.Roots.Clear(); ViewModel.Roots.Add(value); } From dfd040f12fe459e7e942b0ddc607bcf89976ca26 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Tue, 13 Apr 2021 15:41:16 -0700 Subject: [PATCH 048/127] Fix resize behavior of the grid --- src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml index 1f700673e01ee..addbc3112e4ba 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml @@ -40,8 +40,7 @@ From 55cd3fea3bf36f18e5827c6f247c8c8cbc468b79 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Wed, 14 Apr 2021 10:57:24 -0700 Subject: [PATCH 049/127] Correctness change --- .../Core/Def/ValueTracking/ValueTrackingToolWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs index effe48db8d378..d0fbf3a5abccf 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs @@ -14,7 +14,7 @@ namespace Microsoft.VisualStudio.LanguageServices.ValueTracking [Guid(Guids.ValueTrackingToolWindowIdString)] internal class ValueTrackingToolWindow : ToolWindowPane { - private Grid _rootGrid = new(); + private readonly Grid _rootGrid = new(); public static ValueTrackingToolWindow? Instance { get; set; } From e111a834e3d21b2da8ea873913cffcd5096722dd Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Thu, 15 Apr 2021 06:59:16 -0700 Subject: [PATCH 050/127] Enable basic scenarios of consuming abstract static members on type parameters. (#52605) --- .../Portable/Binder/Binder.ValueChecks.cs | 4 +- .../Portable/Binder/Binder_Conversions.cs | 2 +- .../Portable/Binder/Binder_Expressions.cs | 257 +- .../CSharp/Portable/Binder/Binder_Lookup.cs | 6 + .../Portable/Binder/Binder_Operators.cs | 82 +- .../CSharp/Portable/Binder/Binder_Query.cs | 1 + .../Portable/Binder/Binder_Statements.cs | 7 +- .../CSharp/Portable/Binder/Binder_Symbols.cs | 2 +- .../Portable/Binder/Binder_TupleOperators.cs | 5 +- .../CSharp/Portable/Binder/LookupOptions.cs | 5 + .../BinaryOperatorOverloadResolution.cs | 43 +- .../Operators/BinaryOperatorSignature.cs | 15 +- .../UnaryOperatorOverloadResolution.cs | 10 +- .../Operators/UnaryOperatorSignature.cs | 13 +- .../CSharp/Portable/BoundTree/BoundNodes.xml | 4 + .../Portable/BoundTree/BoundTreeRewriter.cs | 2 +- .../CSharp/Portable/BoundTree/Constructors.cs | 26 +- .../Portable/BoundTree/NullabilityRewriter.cs | 4 +- .../BoundTree/TupleBinaryOperatorInfo.cs | 3 + .../CSharp/Portable/CSharpResources.resx | 6 + .../CSharp/Portable/CodeGen/EmitExpression.cs | 201 +- .../CSharp/Portable/CodeGen/Optimizer.cs | 30 +- .../Compiler/MethodBodySynthesizer.cs | 1 + .../CSharp/Portable/Errors/ErrorCode.cs | 2 + .../Generated/BoundNodes.xml.Generated.cs | 70 +- .../DiagnosticsPass_ExpressionTrees.cs | 28 + .../Lowering/DiagnosticsPass_Warnings.cs | 9 +- .../LocalRewriter.PatternLocalRewriter.cs | 4 +- .../LocalRewriter_BinaryOperator.cs | 151 +- .../LocalRewriter/LocalRewriter_Call.cs | 3 +- ...ocalRewriter_CompoundAssignmentOperator.cs | 2 +- .../LocalRewriter_ForEachStatement.cs | 6 + .../LocalRewriter_PointerElementAccess.cs | 1 + .../LocalRewriter/LocalRewriter_Range.cs | 2 +- .../LocalRewriter_StringConcat.cs | 2 +- .../LocalRewriter_TupleBinaryOperator.cs | 10 +- .../LocalRewriter_UnaryOperator.cs | 45 +- .../Portable/Lowering/SpillSequenceSpiller.cs | 6 +- .../Lowering/SyntheticBoundNodeFactory.cs | 4 +- .../SourceUserDefinedOperatorSymbolBase.cs | 12 +- .../Portable/xlf/CSharpResources.cs.xlf | 10 + .../Portable/xlf/CSharpResources.de.xlf | 10 + .../Portable/xlf/CSharpResources.es.xlf | 10 + .../Portable/xlf/CSharpResources.fr.xlf | 10 + .../Portable/xlf/CSharpResources.it.xlf | 10 + .../Portable/xlf/CSharpResources.ja.xlf | 10 + .../Portable/xlf/CSharpResources.ko.xlf | 10 + .../Portable/xlf/CSharpResources.pl.xlf | 10 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 10 + .../Portable/xlf/CSharpResources.ru.xlf | 10 + .../Portable/xlf/CSharpResources.tr.xlf | 10 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 10 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 10 + .../Test/Semantic/Semantics/OperatorTests.cs | 2 +- .../StaticAbstractMembersInInterfacesTests.cs | 3458 +++++++++++++++++ .../Test/Core/TargetFrameworkUtil.cs | 3 + 56 files changed, 4332 insertions(+), 347 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index fa45e0252ad1b..73bfd4d3c36a8 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -1132,7 +1132,7 @@ private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindV return false; } - CheckRuntimeSupportForSymbolAccess(node, receiver, setMethod, diagnostics); + CheckReceiverAndRuntimeSupportForSymbolAccess(node, receiver, setMethod, diagnostics); } } @@ -1176,7 +1176,7 @@ private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindV return false; } - CheckRuntimeSupportForSymbolAccess(node, receiver, getMethod, diagnostics); + CheckReceiverAndRuntimeSupportForSymbolAccess(node, receiver, getMethod, diagnostics); } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 4bf44d7dbdf01..b2472a429b995 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -729,7 +729,7 @@ private bool MemberGroupFinalValidation(BoundExpression? receiverOpt, MethodSymb { if (!IsBadBaseAccess(node, receiverOpt, methodSymbol, diagnostics)) { - CheckRuntimeSupportForSymbolAccess(node, receiverOpt, methodSymbol, diagnostics); + CheckReceiverAndRuntimeSupportForSymbolAccess(node, receiverOpt, methodSymbol, diagnostics); } if (MemberGroupFinalValidationAccessibilityChecks(receiverOpt, methodSymbol, node, diagnostics, invokedAsExtensionMethod)) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index bb69a83783e20..c979f0a89f752 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -6061,8 +6061,7 @@ private BoundExpression BindMemberAccessWithBoundLeft( } var typeArgumentsSyntax = right.Kind() == SyntaxKind.GenericName ? ((GenericNameSyntax)right).TypeArgumentList.Arguments : default(SeparatedSyntaxList); - bool rightHasTypeArguments = typeArgumentsSyntax.Count > 0; - var typeArguments = rightHasTypeArguments ? BindTypeArguments(typeArgumentsSyntax, diagnostics) : default(ImmutableArray); + var typeArguments = typeArgumentsSyntax.Count > 0 ? BindTypeArguments(typeArgumentsSyntax, diagnostics) : default(ImmutableArray); // A member-access consists of a primary-expression, a predefined-type, or a // qualified-alias-member, followed by a "." token, followed by an identifier, @@ -6084,91 +6083,28 @@ private BoundExpression BindMemberAccessWithBoundLeft( var rightName = right.Identifier.ValueText; var rightArity = right.Arity; + BoundExpression result; switch (boundLeft.Kind) { case BoundKind.NamespaceExpression: { - // If K is zero and E is a namespace and E contains a nested namespace with name I, - // then the result is that namespace. - - var ns = ((BoundNamespaceExpression)boundLeft).NamespaceSymbol; - CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); - this.LookupMembersWithFallback(lookupResult, ns, rightName, rightArity, ref useSiteInfo, options: options); - diagnostics.Add(right, useSiteInfo); - - ArrayBuilder symbols = lookupResult.Symbols; - - if (lookupResult.IsMultiViable) + result = tryBindMemberAccessWithBoundNamespaceLeft(((BoundNamespaceExpression)boundLeft).NamespaceSymbol, node, boundLeft, right, diagnostics, lookupResult, options, typeArgumentsSyntax, typeArguments, rightName, rightArity); + if (result is object) { - bool wasError; - Symbol sym = ResultSymbol(lookupResult, rightName, rightArity, node, diagnostics, false, out wasError, ns, options); - if (wasError) - { - return new BoundBadExpression(node, LookupResultKind.Ambiguous, lookupResult.Symbols.AsImmutable(), ImmutableArray.Create(boundLeft), CreateErrorType(rightName), hasErrors: true); - } - else if (sym.Kind == SymbolKind.Namespace) - { - return new BoundNamespaceExpression(node, (NamespaceSymbol)sym); - } - else - { - Debug.Assert(sym.Kind == SymbolKind.NamedType); - var type = (NamedTypeSymbol)sym; - - if (rightHasTypeArguments) - { - type = ConstructNamedTypeUnlessTypeArgumentOmitted(right, type, typeArgumentsSyntax, typeArguments, diagnostics); - } - - ReportDiagnosticsIfObsolete(diagnostics, type, node, hasBaseReceiver: false); - - return new BoundTypeExpression(node, null, type); - } + return result; } - else if (lookupResult.Kind == LookupResultKind.WrongArity) - { - Debug.Assert(symbols.Count > 0); - Debug.Assert(symbols[0].Kind == SymbolKind.NamedType); - - Error(diagnostics, lookupResult.Error, right); - - return new BoundTypeExpression(node, null, - new ExtendedErrorTypeSymbol(GetContainingNamespaceOrType(symbols[0]), symbols.ToImmutable(), lookupResult.Kind, lookupResult.Error, rightArity)); - } - else if (lookupResult.Kind == LookupResultKind.Empty) - { - Debug.Assert(lookupResult.IsClear, "If there's a legitimate reason for having candidates without a reason, then we should produce something intelligent in such cases."); - Debug.Assert(lookupResult.Error == null); - NotFound(node, rightName, rightArity, rightName, diagnostics, aliasOpt: null, qualifierOpt: ns, options: options); - return new BoundBadExpression(node, lookupResult.Kind, symbols.AsImmutable(), ImmutableArray.Create(boundLeft), CreateErrorType(rightName), hasErrors: true); - } break; } case BoundKind.TypeExpression: { - Debug.Assert((object)leftType != null); - if (leftType.TypeKind == TypeKind.TypeParameter) + result = tryBindMemberAccessWithBoundTypeLeft(node, boundLeft, right, invoked, indexed, diagnostics, leftType, lookupResult, options, typeArgumentsSyntax, typeArguments, rightName, rightArity); + if (result is object) { - Error(diagnostics, ErrorCode.ERR_BadSKunknown, boundLeft.Syntax, leftType, MessageID.IDS_SK_TYVAR.Localize()); - return BadExpression(node, LookupResultKind.NotAValue, boundLeft); - } - else if (this.EnclosingNameofArgument == node) - { - // Support selecting an extension method from a type name in nameof(.) - return BindInstanceMemberAccess(node, right, boundLeft, rightName, rightArity, typeArgumentsSyntax, typeArguments, invoked, indexed, diagnostics); - } - else - { - CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); - this.LookupMembersWithFallback(lookupResult, leftType, rightName, rightArity, ref useSiteInfo, basesBeingResolved: null, options: options); - diagnostics.Add(right, useSiteInfo); - if (lookupResult.IsMultiViable) - { - return BindMemberOfType(node, right, rightName, rightArity, indexed, boundLeft, typeArgumentsSyntax, typeArguments, lookupResult, BoundMethodGroupFlags.None, diagnostics: diagnostics); - } + return result; } + break; } case BoundKind.TypeOrValueExpression: @@ -6210,6 +6146,130 @@ private BoundExpression BindMemberAccessWithBoundLeft( { lookupResult.Free(); } + + [MethodImpl(MethodImplOptions.NoInlining)] + BoundExpression tryBindMemberAccessWithBoundNamespaceLeft( + NamespaceSymbol ns, + ExpressionSyntax node, + BoundExpression boundLeft, + SimpleNameSyntax right, + BindingDiagnosticBag diagnostics, + LookupResult lookupResult, + LookupOptions options, + SeparatedSyntaxList typeArgumentsSyntax, + ImmutableArray typeArguments, + string rightName, + int rightArity) + { + // If K is zero and E is a namespace and E contains a nested namespace with name I, + // then the result is that namespace. + + CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); + this.LookupMembersWithFallback(lookupResult, ns, rightName, rightArity, ref useSiteInfo, options: options); + diagnostics.Add(right, useSiteInfo); + + ArrayBuilder symbols = lookupResult.Symbols; + + if (lookupResult.IsMultiViable) + { + bool wasError; + Symbol sym = ResultSymbol(lookupResult, rightName, rightArity, node, diagnostics, false, out wasError, ns, options); + if (wasError) + { + return new BoundBadExpression(node, LookupResultKind.Ambiguous, lookupResult.Symbols.AsImmutable(), ImmutableArray.Create(boundLeft), CreateErrorType(rightName), hasErrors: true); + } + else if (sym.Kind == SymbolKind.Namespace) + { + return new BoundNamespaceExpression(node, (NamespaceSymbol)sym); + } + else + { + Debug.Assert(sym.Kind == SymbolKind.NamedType); + var type = (NamedTypeSymbol)sym; + + if (!typeArguments.IsDefault) + { + type = ConstructNamedTypeUnlessTypeArgumentOmitted(right, type, typeArgumentsSyntax, typeArguments, diagnostics); + } + + ReportDiagnosticsIfObsolete(diagnostics, type, node, hasBaseReceiver: false); + + return new BoundTypeExpression(node, null, type); + } + } + else if (lookupResult.Kind == LookupResultKind.WrongArity) + { + Debug.Assert(symbols.Count > 0); + Debug.Assert(symbols[0].Kind == SymbolKind.NamedType); + + Error(diagnostics, lookupResult.Error, right); + + return new BoundTypeExpression(node, null, + new ExtendedErrorTypeSymbol(GetContainingNamespaceOrType(symbols[0]), symbols.ToImmutable(), lookupResult.Kind, lookupResult.Error, rightArity)); + } + else if (lookupResult.Kind == LookupResultKind.Empty) + { + Debug.Assert(lookupResult.IsClear, "If there's a legitimate reason for having candidates without a reason, then we should produce something intelligent in such cases."); + Debug.Assert(lookupResult.Error == null); + NotFound(node, rightName, rightArity, rightName, diagnostics, aliasOpt: null, qualifierOpt: ns, options: options); + + return new BoundBadExpression(node, lookupResult.Kind, symbols.AsImmutable(), ImmutableArray.Create(boundLeft), CreateErrorType(rightName), hasErrors: true); + } + + return null; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + BoundExpression tryBindMemberAccessWithBoundTypeLeft( + ExpressionSyntax node, + BoundExpression boundLeft, + SimpleNameSyntax right, + bool invoked, + bool indexed, + BindingDiagnosticBag diagnostics, + TypeSymbol leftType, + LookupResult lookupResult, + LookupOptions options, + SeparatedSyntaxList typeArgumentsSyntax, + ImmutableArray typeArguments, + string rightName, + int rightArity) + { + Debug.Assert((object)leftType != null); + if (leftType.TypeKind == TypeKind.TypeParameter) + { + CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); + this.LookupMembersWithFallback(lookupResult, leftType, rightName, rightArity, ref useSiteInfo, basesBeingResolved: null, options: options | LookupOptions.MustNotBeInstance | LookupOptions.MustBeAbstract); + diagnostics.Add(right, useSiteInfo); + if (lookupResult.IsMultiViable) + { + // PROTOTYPE(StaticAbstractMembersInInterfaces): Check language version + return BindMemberOfType(node, right, rightName, rightArity, indexed, boundLeft, typeArgumentsSyntax, typeArguments, lookupResult, BoundMethodGroupFlags.None, diagnostics: diagnostics); + } + else if (lookupResult.IsClear) + { + Error(diagnostics, ErrorCode.ERR_BadSKunknown, boundLeft.Syntax, leftType, MessageID.IDS_SK_TYVAR.Localize()); + return BadExpression(node, LookupResultKind.NotAValue, boundLeft); + } + } + else if (this.EnclosingNameofArgument == node) + { + // Support selecting an extension method from a type name in nameof(.) + return BindInstanceMemberAccess(node, right, boundLeft, rightName, rightArity, typeArgumentsSyntax, typeArguments, invoked, indexed, diagnostics); + } + else + { + CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); + this.LookupMembersWithFallback(lookupResult, leftType, rightName, rightArity, ref useSiteInfo, basesBeingResolved: null, options: options); + diagnostics.Add(right, useSiteInfo); + if (lookupResult.IsMultiViable) + { + return BindMemberOfType(node, right, rightName, rightArity, indexed, boundLeft, typeArgumentsSyntax, typeArguments, lookupResult, BoundMethodGroupFlags.None, diagnostics: diagnostics); + } + } + + return null; + } } private void WarnOnAccessOfOffDefault(SyntaxNode node, BoundExpression boundLeft, BindingDiagnosticBag diagnostics) @@ -6892,7 +6952,7 @@ protected BoundExpression BindFieldAccess( if (!IsBadBaseAccess(node, receiver, fieldSymbol, diagnostics)) { - CheckRuntimeSupportForSymbolAccess(node, receiver, fieldSymbol, diagnostics); + CheckReceiverAndRuntimeSupportForSymbolAccess(node, receiver, fieldSymbol, diagnostics); } TypeSymbol fieldType = fieldSymbol.GetFieldType(this.FieldsBeingBound).Type; @@ -6968,25 +7028,50 @@ private BoundExpression BindPropertyAccess( return new BoundPropertyAccess(node, receiver, propertySymbol, lookupResult, propertySymbol.Type, hasErrors: (hasErrors || hasError)); } - private void CheckRuntimeSupportForSymbolAccess(SyntaxNode node, BoundExpression receiverOpt, Symbol symbol, BindingDiagnosticBag diagnostics) + private void CheckReceiverAndRuntimeSupportForSymbolAccess(SyntaxNode node, BoundExpression receiverOpt, Symbol symbol, BindingDiagnosticBag diagnostics) { - if (symbol.ContainingType?.IsInterface == true && !Compilation.Assembly.RuntimeSupportsDefaultInterfaceImplementation && Compilation.SourceModule != symbol.ContainingModule) + if (symbol.ContainingType?.IsInterface == true) { - if (!symbol.IsStatic && !(symbol is TypeSymbol) && - !symbol.IsImplementableInterfaceMember()) + if (symbol.IsStatic && symbol.IsAbstract) { - Error(diagnostics, ErrorCode.ERR_RuntimeDoesNotSupportDefaultInterfaceImplementation, node); + Debug.Assert(symbol is not TypeSymbol); + + if (receiverOpt is BoundQueryClause { Value: var value }) + { + receiverOpt = value; + } + + if (receiverOpt is not BoundTypeExpression { Type: { TypeKind: TypeKind.TypeParameter } }) + { + Error(diagnostics, ErrorCode.ERR_BadAbstractStaticMemberAccess, node); + return; + } + + if (!Compilation.Assembly.RuntimeSupportsStaticAbstractMembersInInterfaces && Compilation.SourceModule != symbol.ContainingModule) + { + Error(diagnostics, ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, node); + return; + } } - else + + if (!Compilation.Assembly.RuntimeSupportsDefaultInterfaceImplementation && Compilation.SourceModule != symbol.ContainingModule) { - switch (symbol.DeclaredAccessibility) + if (!symbol.IsStatic && !(symbol is TypeSymbol) && + !symbol.IsImplementableInterfaceMember()) { - case Accessibility.Protected: - case Accessibility.ProtectedOrInternal: - case Accessibility.ProtectedAndInternal: + Error(diagnostics, ErrorCode.ERR_RuntimeDoesNotSupportDefaultInterfaceImplementation, node); + } + else + { + switch (symbol.DeclaredAccessibility) + { + case Accessibility.Protected: + case Accessibility.ProtectedOrInternal: + case Accessibility.ProtectedAndInternal: - Error(diagnostics, ErrorCode.ERR_RuntimeDoesNotSupportProtectedAccessForInterfaceMember, node); - break; + Error(diagnostics, ErrorCode.ERR_RuntimeDoesNotSupportProtectedAccessForInterfaceMember, node); + break; + } } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs index 4367d1956e027..b474dbc49b0be 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs @@ -1253,6 +1253,7 @@ internal static ImmutableArray GetCandidateMembers(NamespaceOrTypeSymbol /// internal SingleLookupResult CheckViability(Symbol symbol, int arity, LookupOptions options, TypeSymbol accessThroughType, bool diagnose, ref CompoundUseSiteInfo useSiteInfo, ConsList basesBeingResolved = null) { + Debug.Assert((options & LookupOptions.MustBeAbstract) == 0 || (options & LookupOptions.MustNotBeInstance) != 0); bool inaccessibleViaQualifier; DiagnosticInfo diagInfo; @@ -1268,6 +1269,11 @@ internal SingleLookupResult CheckViability(Symbol symbol, int arity, LookupOptio { return LookupResult.Empty(); } + else if ((options & (LookupOptions.MustNotBeInstance | LookupOptions.MustBeAbstract)) == (LookupOptions.MustNotBeInstance | LookupOptions.MustBeAbstract) && + (unwrappedSymbol is not TypeSymbol && IsInstance(unwrappedSymbol) || !unwrappedSymbol.IsAbstract)) + { + return LookupResult.Empty(); + } else if (WrongArity(symbol, arity, diagnose, options, out diagInfo)) { return LookupResult.WrongArity(symbol, diagInfo); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index f1cd8b3a45564..45932ca8a116f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -169,6 +169,7 @@ private BoundExpression BindCompoundAssignment(AssignmentExpressionSyntax node, BinaryOperatorSignature bestSignature = best.Signature; CheckNativeIntegerFeatureAvailability(bestSignature.Kind, node, diagnostics); + CheckConstraintAndRuntimeSupportForOperator(node, bestSignature.Method, bestSignature.ConstrainedToTypeOpt, diagnostics); if (CheckOverflowAtRuntime) { @@ -177,7 +178,8 @@ private BoundExpression BindCompoundAssignment(AssignmentExpressionSyntax node, bestSignature.LeftType, bestSignature.RightType, bestSignature.ReturnType, - bestSignature.Method); + bestSignature.Method, + bestSignature.ConstrainedToTypeOpt); } BoundExpression rightConverted = CreateConversion(right, best.RightConversion, bestSignature.RightType, diagnostics); @@ -292,7 +294,7 @@ private BoundExpression BindEventAssignment(AssignmentExpressionSyntax node, Bou } else { - CheckRuntimeSupportForSymbolAccess(node, receiverOpt, method, diagnostics); + CheckReceiverAndRuntimeSupportForSymbolAccess(node, receiverOpt, method, diagnostics); } if (eventSymbol.IsWindowsRuntimeEvent) @@ -382,6 +384,12 @@ private BoundExpression BindDynamicBinaryOperator( hasError = true; } + else + { + Debug.Assert(left.Type is not TypeParameterSymbol); + CheckConstraintAndRuntimeSupportForOperator(node, userDefinedOperator, constrainedToTypeOpt: null, diagnostics); + } + diagnostics.Add(node, useSiteInfo); } @@ -392,6 +400,7 @@ private BoundExpression BindDynamicBinaryOperator( right: BindToNaturalType(right, diagnostics), constantValueOpt: ConstantValue.NotAvailable, methodOpt: userDefinedOperator, + constrainedToTypeOpt: null, resultKind: LookupResultKind.Viable, type: Compilation.DynamicType, hasErrors: hasError); @@ -491,7 +500,7 @@ private BoundExpression BindSimpleBinaryOperator(BinaryExpressionSyntax node, Bi // NOTE: no user-defined conversion candidates left = BindToTypeForErrorRecovery(left); right = BindToTypeForErrorRecovery(right); - return new BoundBinaryOperator(node, kind, ConstantValue.NotAvailable, null, LookupResultKind.Empty, left, right, GetBinaryOperatorErrorType(kind, diagnostics, node), true); + return new BoundBinaryOperator(node, kind, ConstantValue.NotAvailable, methodOpt: null, constrainedToTypeOpt: null, LookupResultKind.Empty, left, right, GetBinaryOperatorErrorType(kind, diagnostics, node), true); } TypeSymbol leftType = left.Type; @@ -587,6 +596,7 @@ private BoundExpression BindSimpleBinaryOperator(BinaryExpressionSyntax node, Bi } CheckNativeIntegerFeatureAvailability(resultOperatorKind, node, diagnostics); + CheckConstraintAndRuntimeSupportForOperator(node, signature.Method, signature.ConstrainedToTypeOpt, diagnostics); TypeSymbol resultType = signature.ReturnType; BoundExpression resultLeft = left; @@ -619,6 +629,7 @@ private BoundExpression BindSimpleBinaryOperator(BinaryExpressionSyntax node, Bi resultRight, resultConstant, signature.Method, + signature.ConstrainedToTypeOpt, resultKind, originalUserDefinedOperators, resultType, @@ -803,7 +814,7 @@ private BoundExpression BindConditionalLogicalOperator(BinaryExpressionSyntax no var constantValue = FoldBinaryOperator(node, kind | BinaryOperatorKind.Bool, left, right, SpecialType.System_Boolean, diagnostics); // NOTE: no candidate user-defined operators. - return new BoundBinaryOperator(node, kind | BinaryOperatorKind.Bool, constantValue, methodOpt: null, + return new BoundBinaryOperator(node, kind | BinaryOperatorKind.Bool, constantValue, methodOpt: null, constrainedToTypeOpt: null, resultKind: LookupResultKind.Viable, left, right, type: left.Type, hasErrors: constantValue != null && constantValue.IsBad); } @@ -813,7 +824,7 @@ private BoundExpression BindConditionalLogicalOperator(BinaryExpressionSyntax no if (left.HasAnyErrors || right.HasAnyErrors) { // NOTE: no candidate user-defined operators. - return new BoundBinaryOperator(node, kind, ConstantValue.NotAvailable, methodOpt: null, + return new BoundBinaryOperator(node, kind, ConstantValue.NotAvailable, methodOpt: null, constrainedToTypeOpt: null, resultKind: LookupResultKind.Empty, left, right, type: GetBinaryOperatorErrorType(kind, diagnostics, node), hasErrors: true); } @@ -871,6 +882,9 @@ private BoundExpression BindConditionalLogicalOperator(BinaryExpressionSyntax no { Debug.Assert(trueOperator != null && falseOperator != null); + _ = CheckConstraintAndRuntimeSupportForOperator(node, signature.Method, signature.ConstrainedToTypeOpt, diagnostics) && + CheckConstraintAndRuntimeSupportForOperator(node, kind == BinaryOperatorKind.LogicalAnd ? falseOperator : trueOperator, signature.ConstrainedToTypeOpt, diagnostics); + return new BoundUserDefinedConditionalLogicalOperator( node, resultKind, @@ -879,6 +893,7 @@ private BoundExpression BindConditionalLogicalOperator(BinaryExpressionSyntax no signature.Method, trueOperator, falseOperator, + signature.ConstrainedToTypeOpt, lookupResult, originalUserDefinedOperators, signature.ReturnType); @@ -886,6 +901,7 @@ private BoundExpression BindConditionalLogicalOperator(BinaryExpressionSyntax no else { Debug.Assert(bothBool); + Debug.Assert(!(signature.Method?.ContainingType?.IsInterface ?? false)); return new BoundBinaryOperator( node, @@ -894,6 +910,7 @@ private BoundExpression BindConditionalLogicalOperator(BinaryExpressionSyntax no resultRight, ConstantValue.NotAvailable, signature.Method, + signature.ConstrainedToTypeOpt, lookupResult, originalUserDefinedOperators, signature.ReturnType); @@ -902,7 +919,7 @@ private BoundExpression BindConditionalLogicalOperator(BinaryExpressionSyntax no } // We've already reported the error. - return new BoundBinaryOperator(node, kind, left, right, ConstantValue.NotAvailable, null, lookupResult, originalUserDefinedOperators, CreateErrorType(), true); + return new BoundBinaryOperator(node, kind, left, right, ConstantValue.NotAvailable, methodOpt: null, constrainedToTypeOpt: null, lookupResult, originalUserDefinedOperators, CreateErrorType(), true); } private bool IsValidDynamicCondition(BoundExpression left, bool isNegative, ref CompoundUseSiteInfo useSiteInfo, out MethodSymbol userDefinedOperator) @@ -994,8 +1011,10 @@ private bool IsValidUserDefinedConditionalLogicalOperator( // As mentioned above, we relax this restriction. The types must all be the same. bool typesAreSame = TypeSymbol.Equals(signature.LeftType, signature.RightType, TypeCompareKind.ConsiderEverything2) && TypeSymbol.Equals(signature.LeftType, signature.ReturnType, TypeCompareKind.ConsiderEverything2); - bool typeMatchesContainer = TypeSymbol.Equals(signature.ReturnType, t, TypeCompareKind.ConsiderEverything2) || - signature.ReturnType.IsNullableType() && TypeSymbol.Equals(signature.ReturnType.GetNullableUnderlyingType(), t, TypeCompareKind.ConsiderEverything2); + MethodSymbol definition; + bool typeMatchesContainer = TypeSymbol.Equals(signature.ReturnType.StrippedType(), t, TypeCompareKind.ConsiderEverything2) || + (t.IsInterface && signature.Method.IsAbstract && + SourceUserDefinedOperatorSymbol.IsSelfConstrainedTypeParameter((definition = signature.Method.OriginalDefinition).ReturnType.StrippedType(), definition.ContainingType)); if (!typesAreSame || !typeMatchesContainer) { @@ -2087,7 +2106,8 @@ private BoundExpression BindIncrementOperator(CSharpSyntaxNode node, ExpressionS node, kind, operand, - null, + methodOpt: null, + constrainedToTypeOpt: null, Conversion.NoConversion, Conversion.NoConversion, LookupResultKind.Empty, @@ -2106,6 +2126,7 @@ private BoundExpression BindIncrementOperator(CSharpSyntaxNode node, ExpressionS kind.WithType(UnaryOperatorKind.Dynamic).WithOverflowChecksIfApplicable(CheckOverflowAtRuntime), operand, methodOpt: null, + constrainedToTypeOpt: null, operandConversion: Conversion.NoConversion, resultConversion: Conversion.NoConversion, resultKind: LookupResultKind.Viable, @@ -2124,7 +2145,8 @@ private BoundExpression BindIncrementOperator(CSharpSyntaxNode node, ExpressionS node, kind, operand, - null, + methodOpt: null, + constrainedToTypeOpt: null, Conversion.NoConversion, Conversion.NoConversion, resultKind, @@ -2136,6 +2158,7 @@ private BoundExpression BindIncrementOperator(CSharpSyntaxNode node, ExpressionS var signature = best.Signature; CheckNativeIntegerFeatureAvailability(signature.Kind, node, diagnostics); + CheckConstraintAndRuntimeSupportForOperator(node, signature.Method, signature.ConstrainedToTypeOpt, diagnostics); CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); var resultConversion = Conversions.ClassifyConversionFromType(signature.ReturnType, operandType, ref useSiteInfo); @@ -2167,6 +2190,7 @@ private BoundExpression BindIncrementOperator(CSharpSyntaxNode node, ExpressionS signature.Kind.WithOverflowChecksIfApplicable(CheckOverflowAtRuntime), operand, signature.Method, + signature.ConstrainedToTypeOpt, operandConversion, resultConversion, resultKind, @@ -2175,6 +2199,34 @@ private BoundExpression BindIncrementOperator(CSharpSyntaxNode node, ExpressionS hasErrors); } +#nullable enable + /// + /// Returns false if reported an error, true otherwise. + /// + private bool CheckConstraintAndRuntimeSupportForOperator(SyntaxNode node, MethodSymbol? methodOpt, TypeSymbol? constrainedToTypeOpt, BindingDiagnosticBag diagnostics) + { + if (methodOpt?.ContainingType?.IsInterface == true) + { + if (methodOpt.IsStatic && methodOpt.IsAbstract) + { + if (constrainedToTypeOpt is not TypeParameterSymbol) + { + Error(diagnostics, ErrorCode.ERR_BadAbstractStaticMemberAccess, node); + return false; + } + + if (!Compilation.Assembly.RuntimeSupportsStaticAbstractMembersInInterfaces && Compilation.SourceModule != methodOpt.ContainingModule) + { + Error(diagnostics, ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, node); + return false; + } + } + } + + return true; + } +#nullable disable + private BoundExpression BindSuppressNullableWarningExpression(PostfixUnaryExpressionSyntax node, BindingDiagnosticBag diagnostics) { var expr = BindExpression(node.Operand, diagnostics); @@ -2456,6 +2508,7 @@ private BoundExpression BindUnaryOperatorCore(CSharpSyntaxNode node, string oper // Note: no candidate user-defined operators. return new BoundUnaryOperator(node, kind, operand, ConstantValue.NotAvailable, methodOpt: null, + constrainedToTypeOpt: null, resultKind: LookupResultKind.Empty, type: CreateErrorType(), hasErrors: true); @@ -2476,6 +2529,7 @@ private BoundExpression BindUnaryOperatorCore(CSharpSyntaxNode node, string oper operand: operand, constantValueOpt: ConstantValue.NotAvailable, methodOpt: null, + constrainedToTypeOpt: null, resultKind: LookupResultKind.Viable, type: operand.Type!); } @@ -2491,7 +2545,8 @@ private BoundExpression BindUnaryOperatorCore(CSharpSyntaxNode node, string oper kind, operand, ConstantValue.NotAvailable, - null, + methodOpt: null, + constrainedToTypeOpt: null, resultKind, originalUserDefinedOperators, CreateErrorType(), @@ -2503,17 +2558,18 @@ private BoundExpression BindUnaryOperatorCore(CSharpSyntaxNode node, string oper var resultOperand = CreateConversion(operand.Syntax, operand, best.Conversion, isCast: false, conversionGroupOpt: null, signature.OperandType, diagnostics); var resultType = signature.ReturnType; UnaryOperatorKind resultOperatorKind = signature.Kind; - var resultMethod = signature.Method; var resultConstant = FoldUnaryOperator(node, resultOperatorKind, resultOperand, resultType.SpecialType, diagnostics); CheckNativeIntegerFeatureAvailability(resultOperatorKind, node, diagnostics); + CheckConstraintAndRuntimeSupportForOperator(node, signature.Method, signature.ConstrainedToTypeOpt, diagnostics); return new BoundUnaryOperator( node, resultOperatorKind.WithOverflowChecksIfApplicable(CheckOverflowAtRuntime), resultOperand, resultConstant, - resultMethod, + signature.Method, + signature.ConstrainedToTypeOpt, resultKind, resultType); } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs index dcf551b0e78c0..cec4b3a6f2145 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs @@ -780,6 +780,7 @@ protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression r { if (ultimateReceiver.Type.TypeKind == TypeKind.TypeParameter) { + // PROTOTYPE(StaticAbstractMembersInInterfaces): Do we really want to enable usage of static abstract members here? Error(diagnostics, ErrorCode.ERR_BadSKunknown, ultimateReceiver.Syntax, ultimateReceiver.Type, MessageID.IDS_SK_TYVAR.Localize()); } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 61c74b3b5bb33..a7c425a64ac0f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -2364,7 +2364,8 @@ internal BoundExpression BindBooleanExpression(ExpressionSyntax node, BindingDia UnaryOperatorKind.DynamicTrue, BindToNaturalType(expr, diagnostics), ConstantValue.NotAvailable, - null, + methodOpt: null, + constrainedToTypeOpt: null, LookupResultKind.Viable, boolean) { @@ -2430,10 +2431,12 @@ internal BoundExpression BindBooleanExpression(ExpressionSyntax node, BindingDia destination: best.Signature.OperandType, diagnostics: diagnostics); + CheckConstraintAndRuntimeSupportForOperator(node, signature.Method, signature.ConstrainedToTypeOpt, diagnostics); + // Consider op_true to be compiler-generated so that it doesn't appear in the semantic model. // UNDONE: If we decide to expose the operator in the semantic model, we'll have to remove the // WasCompilerGenerated flag (and possibly suppress the symbol in specific APIs). - return new BoundUnaryOperator(node, signature.Kind, resultOperand, ConstantValue.NotAvailable, signature.Method, resultKind, originalUserDefinedOperators, signature.ReturnType) + return new BoundUnaryOperator(node, signature.Kind, resultOperand, ConstantValue.NotAvailable, signature.Method, signature.ConstrainedToTypeOpt, resultKind, originalUserDefinedOperators, signature.ReturnType) { WasCompilerGenerated = true }; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index 9438b31a20593..f2c30be9653d3 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -1700,7 +1700,7 @@ internal Symbol ResultSymbol( if (symbol.Kind == SymbolKind.NamedType) { - CheckRuntimeSupportForSymbolAccess(where, receiverOpt: null, symbol, diagnostics); + CheckReceiverAndRuntimeSupportForSymbolAccess(where, receiverOpt: null, symbol, diagnostics); if (suppressUseSiteDiagnostics && diagnostics.DependenciesBag is object) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs index c89dfcf6285ce..1eca4037574cf 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs @@ -104,7 +104,8 @@ private TupleBinaryOperatorInfo BindTupleBinaryOperatorInfo(BinaryExpressionSynt case BoundBinaryOperator binary: PrepareBoolConversionAndTruthOperator(binary.Type, node, kind, diagnostics, out Conversion conversionIntoBoolOperator, out UnaryOperatorSignature boolOperator); - return new TupleBinaryOperatorInfo.Single(binary.Left.Type, binary.Right.Type, binary.OperatorKind, binary.MethodOpt, conversionIntoBoolOperator, boolOperator); + // PROTOTYPE(StaticAbstractMembersInInterfaces): Ensure we have a unit-test for this code path. + return new TupleBinaryOperatorInfo.Single(binary.Left.Type, binary.Right.Type, binary.OperatorKind, binary.MethodOpt, binary.ConstrainedToTypeOpt, conversionIntoBoolOperator, boolOperator); default: throw ExceptionUtilities.UnexpectedValue(comparison); @@ -189,7 +190,7 @@ private TupleBinaryOperatorInfo BindTupleDynamicBinaryOperatorSingleInfo(BinaryE // We'll want to dynamically invoke operators op_true (/op_false) for equality (/inequality) comparison, but we don't need // to prepare either a conversion or a truth operator. Those can just be synthesized during lowering. return new TupleBinaryOperatorInfo.Single(dynamicType, dynamicType, elementOperatorKind, - methodSymbolOpt: null, conversionForBool: Conversion.Identity, boolOperator: default); + methodSymbolOpt: null, constrainedToTypeOpt: null, conversionForBool: Conversion.Identity, boolOperator: default); } private TupleBinaryOperatorInfo.Multiple BindTupleBinaryOperatorNestedInfo(BinaryExpressionSyntax node, BinaryOperatorKind kind, diff --git a/src/Compilers/CSharp/Portable/Binder/LookupOptions.cs b/src/Compilers/CSharp/Portable/Binder/LookupOptions.cs index 5d0594144b5de..881d467e40bc0 100644 --- a/src/Compilers/CSharp/Portable/Binder/LookupOptions.cs +++ b/src/Compilers/CSharp/Portable/Binder/LookupOptions.cs @@ -100,6 +100,11 @@ internal enum LookupOptions /// Do not consider symbols that are method type parameters. /// MustNotBeMethodTypeParameter = 1 << 14, + + /// + /// Consider only symbols that are abstract. + /// + MustBeAbstract = 1 << 15, } internal static class LookupOptionExtensions diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs index 81ba41a7dbe20..a1109ecbcf3c6 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs @@ -122,18 +122,40 @@ internal void BinaryOperatorOverloadResolution_NoEasyOut(BinaryOperatorKind kind string name = OperatorFacts.BinaryOperatorNameFromOperatorKind(kind); var lookedInInterfaces = PooledDictionary.GetInstance(); + TypeSymbol firstOperatorSourceOpt; + TypeSymbol secondOperatorSourceOpt; + bool firstSourceIsInterface; + bool secondSourceIsInterface; + + // Always start lookup from a type parameter. This ensures that regardless of the order we always pick up constrained type for + // each distinct candidate operator. + if (leftOperatorSourceOpt is null || (leftOperatorSourceOpt is not TypeParameterSymbol && rightOperatorSourceOpt is TypeParameterSymbol)) + { + firstOperatorSourceOpt = rightOperatorSourceOpt; + secondOperatorSourceOpt = leftOperatorSourceOpt; + firstSourceIsInterface = rightSourceIsInterface; + secondSourceIsInterface = leftSourceIsInterface; + } + else + { + firstOperatorSourceOpt = leftOperatorSourceOpt; + secondOperatorSourceOpt = rightOperatorSourceOpt; + firstSourceIsInterface = leftSourceIsInterface; + secondSourceIsInterface = rightSourceIsInterface; + } + hadApplicableCandidates = GetUserDefinedBinaryOperatorsFromInterfaces(kind, name, - leftOperatorSourceOpt, leftSourceIsInterface, left, right, ref useSiteInfo, lookedInInterfaces, result.Results); + firstOperatorSourceOpt, firstSourceIsInterface, left, right, ref useSiteInfo, lookedInInterfaces, result.Results); if (!hadApplicableCandidates) { result.Results.Clear(); } - if ((object)rightOperatorSourceOpt != null && !rightOperatorSourceOpt.Equals(leftOperatorSourceOpt)) + if ((object)secondOperatorSourceOpt != null && !secondOperatorSourceOpt.Equals(firstOperatorSourceOpt)) { var rightOperators = ArrayBuilder.GetInstance(); if (GetUserDefinedBinaryOperatorsFromInterfaces(kind, name, - rightOperatorSourceOpt, rightSourceIsInterface, left, right, ref useSiteInfo, lookedInInterfaces, rightOperators)) + secondOperatorSourceOpt, secondSourceIsInterface, left, right, ref useSiteInfo, lookedInInterfaces, rightOperators)) { hadApplicableCandidates = true; AddDistinctOperators(result.Results, rightOperators); @@ -194,6 +216,7 @@ private bool GetUserDefinedBinaryOperatorsFromInterfaces(BinaryOperatorKind kind bool hadUserDefinedCandidateFromInterfaces = false; ImmutableArray interfaces = default; + TypeSymbol constrainedToTypeOpt = null; if (sourceIsInterface) { @@ -201,7 +224,7 @@ private bool GetUserDefinedBinaryOperatorsFromInterfaces(BinaryOperatorKind kind if (!lookedInInterfaces.TryGetValue(operatorSourceOpt, out _)) { var operators = ArrayBuilder.GetInstance(); - GetUserDefinedBinaryOperatorsFromType((NamedTypeSymbol)operatorSourceOpt, kind, name, operators); + GetUserDefinedBinaryOperatorsFromType(constrainedToTypeOpt, (NamedTypeSymbol)operatorSourceOpt, kind, name, operators); hadUserDefinedCandidateFromInterfaces = CandidateOperators(operators, left, right, candidates, ref useSiteInfo); operators.Free(); Debug.Assert(hadUserDefinedCandidateFromInterfaces == candidates.Any(r => r.IsValid)); @@ -217,6 +240,7 @@ private bool GetUserDefinedBinaryOperatorsFromInterfaces(BinaryOperatorKind kind else if (operatorSourceOpt.IsTypeParameter()) { interfaces = ((TypeParameterSymbol)operatorSourceOpt).AllEffectiveInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo); + constrainedToTypeOpt = operatorSourceOpt; } if (!interfaces.IsDefaultOrEmpty) @@ -253,7 +277,7 @@ private bool GetUserDefinedBinaryOperatorsFromInterfaces(BinaryOperatorKind kind operators.Clear(); results.Clear(); - GetUserDefinedBinaryOperatorsFromType(@interface, kind, name, operators); + GetUserDefinedBinaryOperatorsFromType(constrainedToTypeOpt, @interface, kind, name, operators); hadUserDefinedCandidate = CandidateOperators(operators, left, right, results, ref useSiteInfo); Debug.Assert(hadUserDefinedCandidate == results.Any(r => r.IsValid)); lookedInInterfaces.Add(@interface, hadUserDefinedCandidate); @@ -832,7 +856,7 @@ private bool GetUserDefinedOperators( for (; (object)current != null; current = current.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteInfo)) { operators.Clear(); - GetUserDefinedBinaryOperatorsFromType(current, kind, name, operators); + GetUserDefinedBinaryOperatorsFromType(constrainedToTypeOpt: null, current, kind, name, operators); results.Clear(); if (CandidateOperators(operators, left, right, results, ref useSiteInfo)) { @@ -848,6 +872,7 @@ private bool GetUserDefinedOperators( } private void GetUserDefinedBinaryOperatorsFromType( + TypeSymbol constrainedToTypeOpt, NamedTypeSymbol type, BinaryOperatorKind kind, string name, @@ -865,7 +890,7 @@ private void GetUserDefinedBinaryOperatorsFromType( TypeSymbol rightOperandType = op.GetParameterType(1); TypeSymbol resultType = op.ReturnType; - operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.UserDefined | kind, leftOperandType, rightOperandType, resultType, op)); + operators.Add(new BinaryOperatorSignature(BinaryOperatorKind.UserDefined | kind, leftOperandType, rightOperandType, resultType, op, constrainedToTypeOpt)); LiftingResult lifting = UserDefinedBinaryOperatorCanBeLifted(leftOperandType, rightOperandType, resultType, kind); @@ -873,13 +898,13 @@ private void GetUserDefinedBinaryOperatorsFromType( { operators.Add(new BinaryOperatorSignature( BinaryOperatorKind.Lifted | BinaryOperatorKind.UserDefined | kind, - MakeNullable(leftOperandType), MakeNullable(rightOperandType), MakeNullable(resultType), op)); + MakeNullable(leftOperandType), MakeNullable(rightOperandType), MakeNullable(resultType), op, constrainedToTypeOpt)); } else if (lifting == LiftingResult.LiftOperandsButNotResult) { operators.Add(new BinaryOperatorSignature( BinaryOperatorKind.Lifted | BinaryOperatorKind.UserDefined | kind, - MakeNullable(leftOperandType), MakeNullable(rightOperandType), resultType, op)); + MakeNullable(leftOperandType), MakeNullable(rightOperandType), resultType, op, constrainedToTypeOpt)); } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorSignature.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorSignature.cs index d406f5ecda199..ed0cc94d016ad 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorSignature.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorSignature.cs @@ -19,6 +19,7 @@ internal struct BinaryOperatorSignature : IEquatable public readonly TypeSymbol RightType; public readonly TypeSymbol ReturnType; public readonly MethodSymbol Method; + public readonly TypeSymbol ConstrainedToTypeOpt; public readonly BinaryOperatorKind Kind; /// @@ -28,13 +29,25 @@ internal struct BinaryOperatorSignature : IEquatable /// public int? Priority; - public BinaryOperatorSignature(BinaryOperatorKind kind, TypeSymbol leftType, TypeSymbol rightType, TypeSymbol returnType, MethodSymbol method = null) + public BinaryOperatorSignature(BinaryOperatorKind kind, TypeSymbol leftType, TypeSymbol rightType, TypeSymbol returnType) + { + this.Kind = kind; + this.LeftType = leftType; + this.RightType = rightType; + this.ReturnType = returnType; + this.Method = null; + this.ConstrainedToTypeOpt = null; + this.Priority = null; + } + + public BinaryOperatorSignature(BinaryOperatorKind kind, TypeSymbol leftType, TypeSymbol rightType, TypeSymbol returnType, MethodSymbol method, TypeSymbol constrainedToTypeOpt) { this.Kind = kind; this.LeftType = leftType; this.RightType = rightType; this.ReturnType = returnType; this.Method = method; + this.ConstrainedToTypeOpt = constrainedToTypeOpt; this.Priority = null; } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs index e4341aa2b59b7..68f3de987afe9 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs @@ -375,6 +375,7 @@ private bool GetUserDefinedOperators(UnaryOperatorKind kind, BoundExpression ope // we stop looking. TypeSymbol type0 = operand.Type.StrippedType(); + TypeSymbol constrainedToTypeOpt = type0 as TypeParameterSymbol; // Searching for user-defined operators is expensive; let's take an early out if we can. if (OperatorFacts.DefinitelyHasNoUserDefinedOperators(type0)) @@ -400,7 +401,7 @@ private bool GetUserDefinedOperators(UnaryOperatorKind kind, BoundExpression ope for (; (object)current != null; current = current.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteInfo)) { operators.Clear(); - GetUserDefinedUnaryOperatorsFromType(current, kind, name, operators); + GetUserDefinedUnaryOperatorsFromType(constrainedToTypeOpt, current, kind, name, operators); results.Clear(); if (CandidateOperators(operators, operand, results, ref useSiteInfo)) { @@ -444,7 +445,7 @@ private bool GetUserDefinedOperators(UnaryOperatorKind kind, BoundExpression ope operators.Clear(); resultsFromInterface.Clear(); - GetUserDefinedUnaryOperatorsFromType(@interface, kind, name, operators); + GetUserDefinedUnaryOperatorsFromType(constrainedToTypeOpt, @interface, kind, name, operators); if (CandidateOperators(operators, operand, resultsFromInterface, ref useSiteInfo)) { hadApplicableCandidates = true; @@ -466,6 +467,7 @@ private bool GetUserDefinedOperators(UnaryOperatorKind kind, BoundExpression ope } private void GetUserDefinedUnaryOperatorsFromType( + TypeSymbol constrainedToTypeOpt, NamedTypeSymbol type, UnaryOperatorKind kind, string name, @@ -482,7 +484,7 @@ private void GetUserDefinedUnaryOperatorsFromType( TypeSymbol operandType = op.GetParameterType(0); TypeSymbol resultType = op.ReturnType; - operators.Add(new UnaryOperatorSignature(UnaryOperatorKind.UserDefined | kind, operandType, resultType, op)); + operators.Add(new UnaryOperatorSignature(UnaryOperatorKind.UserDefined | kind, operandType, resultType, op, constrainedToTypeOpt)); // SPEC: For the unary operators + ++ - -- ! ~ a lifted form of an operator exists // SPEC: if the operand and its result types are both non-nullable value types. @@ -503,7 +505,7 @@ private void GetUserDefinedUnaryOperatorsFromType( { operators.Add(new UnaryOperatorSignature( UnaryOperatorKind.Lifted | UnaryOperatorKind.UserDefined | kind, - MakeNullable(operandType), MakeNullable(resultType), op)); + MakeNullable(operandType), MakeNullable(resultType), op, constrainedToTypeOpt)); } break; } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorSignature.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorSignature.cs index 865a30b4ccaf7..b646f848a65e9 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorSignature.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorSignature.cs @@ -15,16 +15,27 @@ internal struct UnaryOperatorSignature public static UnaryOperatorSignature Error = default(UnaryOperatorSignature); public readonly MethodSymbol Method; + public readonly TypeSymbol ConstrainedToTypeOpt; public readonly TypeSymbol OperandType; public readonly TypeSymbol ReturnType; public readonly UnaryOperatorKind Kind; - public UnaryOperatorSignature(UnaryOperatorKind kind, TypeSymbol operandType, TypeSymbol returnType, MethodSymbol method = null) + public UnaryOperatorSignature(UnaryOperatorKind kind, TypeSymbol operandType, TypeSymbol returnType) + { + this.Kind = kind; + this.OperandType = operandType; + this.ReturnType = returnType; + this.Method = null; + this.ConstrainedToTypeOpt = null; + } + + public UnaryOperatorSignature(UnaryOperatorKind kind, TypeSymbol operandType, TypeSymbol returnType, MethodSymbol method, TypeSymbol constrainedToTypeOpt) { this.Kind = kind; this.OperandType = operandType; this.ReturnType = returnType; this.Method = method; + this.ConstrainedToTypeOpt = constrainedToTypeOpt; } public override string ToString() diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 783c7198029d9..b24063146627a 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -280,6 +280,7 @@ + + diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitConversion.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitConversion.cs index db65e2870aa69..61309792dc959 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitConversion.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitConversion.cs @@ -324,6 +324,17 @@ private void EmitDelegateCreation(BoundExpression node, BoundExpression receiver if (isStatic) { _builder.EmitNullConstant(); + + if (method.IsAbstract) + { + if (receiver is not BoundTypeExpression { Type: { TypeKind: TypeKind.TypeParameter } }) + { + throw ExceptionUtilities.Unreachable; + } + + _builder.EmitOpCode(ILOpCode.Constrained); + EmitSymbolToken(receiver.Type, receiver.Syntax); + } } else { @@ -339,7 +350,7 @@ private void EmitDelegateCreation(BoundExpression node, BoundExpression receiver // Metadata Spec (II.14.6): // Delegates shall be declared sealed. // The Invoke method shall be virtual. - if (method.IsMetadataVirtual() && !method.ContainingType.IsDelegateType() && !receiver.SuppressVirtualCalls) + if (!method.IsStatic && method.IsMetadataVirtual() && !method.ContainingType.IsDelegateType() && !receiver.SuppressVirtualCalls) { // NOTE: method.IsMetadataVirtual -> receiver != null _builder.EmitOpCode(ILOpCode.Dup); diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs index b13f146fa6b4f..a57d88fd832c5 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs @@ -1525,7 +1525,7 @@ private void EmitStaticCallExpression(BoundCall call, UseKind useKind) Debug.Assert(method.IsStatic); EmitArguments(arguments, method.Parameters, call.ArgumentRefKindsOpt); - int stackBehavior = GetCallStackBehavior(call.Method, arguments); + int stackBehavior = GetCallStackBehavior(method, arguments); if (method.IsAbstract) { @@ -1685,7 +1685,7 @@ private void EmitInstanceCallExpression(BoundCall call, UseKind useKind) } EmitArguments(arguments, method.Parameters, call.ArgumentRefKindsOpt); - int stackBehavior = GetCallStackBehavior(call.Method, arguments); + int stackBehavior = GetCallStackBehavior(method, arguments); switch (callKind) { case CallKind.Call: @@ -1830,7 +1830,7 @@ internal static bool MayUseCallForStructMethod(MethodSymbol method) { Debug.Assert(method.ContainingType.IsVerifierValue(), "this is not a value type"); - if (!method.IsMetadataVirtual()) + if (!method.IsMetadataVirtual() || method.IsStatic) { return true; } @@ -3519,6 +3519,17 @@ private void EmitLoadFunction(BoundFunctionPointerLoad load, bool used) if (used) { + if (load.TargetMethod.IsAbstract && load.TargetMethod.IsStatic) + { + if (load.ConstrainedToTypeOpt is not { TypeKind: TypeKind.TypeParameter }) + { + throw ExceptionUtilities.Unreachable; + } + + _builder.EmitOpCode(ILOpCode.Constrained); + EmitSymbolToken(load.ConstrainedToTypeOpt, load.Syntax); + } + _builder.EmitOpCode(ILOpCode.Ldftn); EmitSymbolToken(load.TargetMethod, load.Syntax, optArgList: null); } diff --git a/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs b/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs index e666c9049deff..d7ced20883e10 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs @@ -2076,8 +2076,7 @@ public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) // do actual assignment - // PROTOTYPE(StaticAbstractMembersInInterfaces): This assert fires for a user-defined prefix increment/decrement. Open an issue. - //Debug.Assert(locInfo.LocalDefs.Any((d) => _nodeCounter == d.Start && _nodeCounter <= d.End)); + Debug.Assert(locInfo.LocalDefs.Any((d) => _nodeCounter == d.Start && _nodeCounter <= d.End)); var isLast = IsLastAccess(locInfo, _nodeCounter); if (isLast) @@ -2093,6 +2092,36 @@ public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) } } +#nullable enable + public override BoundNode VisitCall(BoundCall node) + { + BoundExpression? receiverOpt = node.ReceiverOpt; + + if (node.Method.RequiresInstanceReceiver) + { + receiverOpt = (BoundExpression?)this.Visit(receiverOpt); + } + else + { + _nodeCounter++; + + if (receiverOpt is BoundTypeExpression { AliasOpt: null, BoundContainingTypeOpt: null, BoundDimensionsOpt: { IsEmpty: true }, Type: { TypeKind: TypeKind.TypeParameter } } typeExpression) + { + receiverOpt = typeExpression.Update(aliasOpt: null, boundContainingTypeOpt: null, boundDimensionsOpt: ImmutableArray.Empty, + typeWithAnnotations: typeExpression.TypeWithAnnotations, type: this.VisitType(typeExpression.Type)); + } + else if (receiverOpt is not null) + { + throw ExceptionUtilities.Unreachable; + } + } + + ImmutableArray arguments = this.VisitList(node.Arguments); + TypeSymbol? type = this.VisitType(node.Type); + return node.Update(receiverOpt, node.Method, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.IsDelegateCall, node.Expanded, node.InvokedAsExtensionMethod, node.ArgsToParamsOpt, node.DefaultArguments, node.ResultKind, node.OriginalMethodsOpt, type); + } +#nullable disable + public override BoundNode VisitCatchBlock(BoundCatchBlock node) { var exceptionSource = node.ExceptionSourceOpt; diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index 64b1d9a3be142..3ecaed56ec5fb 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -1042,7 +1042,7 @@ public BoundUnconvertedAddressOfOperator Update(BoundMethodGroup operand) internal sealed partial class BoundFunctionPointerLoad : BoundExpression { - public BoundFunctionPointerLoad(SyntaxNode syntax, MethodSymbol targetMethod, TypeSymbol type, bool hasErrors) + public BoundFunctionPointerLoad(SyntaxNode syntax, MethodSymbol targetMethod, TypeSymbol? constrainedToTypeOpt, TypeSymbol type, bool hasErrors) : base(BoundKind.FunctionPointerLoad, syntax, type, hasErrors) { @@ -1050,9 +1050,10 @@ public BoundFunctionPointerLoad(SyntaxNode syntax, MethodSymbol targetMethod, Ty RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); this.TargetMethod = targetMethod; + this.ConstrainedToTypeOpt = constrainedToTypeOpt; } - public BoundFunctionPointerLoad(SyntaxNode syntax, MethodSymbol targetMethod, TypeSymbol type) + public BoundFunctionPointerLoad(SyntaxNode syntax, MethodSymbol targetMethod, TypeSymbol? constrainedToTypeOpt, TypeSymbol type) : base(BoundKind.FunctionPointerLoad, syntax, type) { @@ -1060,20 +1061,23 @@ public BoundFunctionPointerLoad(SyntaxNode syntax, MethodSymbol targetMethod, Ty RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); this.TargetMethod = targetMethod; + this.ConstrainedToTypeOpt = constrainedToTypeOpt; } public MethodSymbol TargetMethod { get; } + public TypeSymbol? ConstrainedToTypeOpt { get; } + public new TypeSymbol Type => base.Type!; [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitFunctionPointerLoad(this); - public BoundFunctionPointerLoad Update(MethodSymbol targetMethod, TypeSymbol type) + public BoundFunctionPointerLoad Update(MethodSymbol targetMethod, TypeSymbol? constrainedToTypeOpt, TypeSymbol type) { - if (!Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(targetMethod, this.TargetMethod) || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (!Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(targetMethod, this.TargetMethod) || !TypeSymbol.Equals(constrainedToTypeOpt, this.ConstrainedToTypeOpt, TypeCompareKind.ConsiderEverything) || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundFunctionPointerLoad(this.Syntax, targetMethod, type, this.HasErrors); + var result = new BoundFunctionPointerLoad(this.Syntax, targetMethod, constrainedToTypeOpt, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -9877,8 +9881,9 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitFunctionPointerLoad(BoundFunctionPointerLoad node) { + TypeSymbol? constrainedToTypeOpt = this.VisitType(node.ConstrainedToTypeOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.TargetMethod, type); + return node.Update(node.TargetMethod, constrainedToTypeOpt, type); } public override BoundNode? VisitPointerIndirectionOperator(BoundPointerIndirectionOperator node) { @@ -11226,16 +11231,17 @@ public NullabilityRewriter(ImmutableDictionary new TreeDumperNode("functionPointerLoad", null, new TreeDumperNode[] { new TreeDumperNode("targetMethod", node.TargetMethod, null), + new TreeDumperNode("constrainedToTypeOpt", node.ConstrainedToTypeOpt, null), new TreeDumperNode("type", node.Type, null), new TreeDumperNode("isSuppressed", node.IsSuppressed, null), new TreeDumperNode("hasErrors", node.HasErrors, null) diff --git a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs index 8bcaf2a556f37..4747716dec103 100644 --- a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs @@ -1334,7 +1334,7 @@ public override BoundNode VisitFunctionPointerLoad(BoundFunctionPointerLoad node receiver.Kind == BoundKind.TypeExpression && remappedMethod is { RequiresInstanceReceiver: false, IsStatic: true }); - return node.Update(remappedMethod, node.Type); + return node.Update(remappedMethod, constrainedToTypeOpt: node.ConstrainedToTypeOpt, node.Type); } return base.VisitFunctionPointerLoad(node); diff --git a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs index be951b99f2fc5..1cd7b452dfa1c 100644 --- a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs +++ b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs @@ -785,6 +785,10 @@ private void CheckMethodGroup(BoundMethodGroup node, MethodSymbol method, bool p { Error(ErrorCode.ERR_AddressOfMethodGroupInExpressionTree, node); } + else if (method is not null && method.IsAbstract && method.IsStatic) + { + Error(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, node); + } } CheckReceiverIfField(node.ReceiverOpt); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index 2ba21b8e52173..f0677f4442d72 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -379,7 +379,9 @@ private BoundExpression MakeConversionNodeCore( { var mg = (BoundMethodGroup)rewrittenOperand; Debug.Assert(oldNodeOpt.SymbolOpt is { }); - return new BoundFunctionPointerLoad(oldNodeOpt.Syntax, oldNodeOpt.SymbolOpt, type: funcPtrType, hasErrors: false); + return new BoundFunctionPointerLoad(oldNodeOpt.Syntax, oldNodeOpt.SymbolOpt, + constrainedToTypeOpt: oldNodeOpt.SymbolOpt.IsStatic && oldNodeOpt.SymbolOpt.IsAbstract ? mg.ReceiverOpt?.Type : null, + type: funcPtrType, hasErrors: false); } case ConversionKind.MethodGroup: @@ -391,7 +393,7 @@ private BoundExpression MakeConversionNodeCore( Debug.Assert(method is { }); var oldSyntax = _factory.Syntax; _factory.Syntax = (mg.ReceiverOpt ?? mg).Syntax; - var receiver = (!method.RequiresInstanceReceiver && !oldNodeOpt.IsExtensionMethod) ? _factory.Type(method.ContainingType) : mg.ReceiverOpt; + var receiver = (!method.RequiresInstanceReceiver && !oldNodeOpt.IsExtensionMethod && !method.IsAbstract) ? _factory.Type(method.ContainingType) : mg.ReceiverOpt; Debug.Assert(receiver is { }); _factory.Syntax = oldSyntax; return new BoundDelegateCreationExpression(syntax, argument: receiver, methodOpt: method, diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DelegateCreationExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DelegateCreationExpression.cs index 30c38120e4b9c..de93a6632a1a2 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DelegateCreationExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DelegateCreationExpression.cs @@ -30,7 +30,7 @@ public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationE Debug.Assert(method is { }); var oldSyntax = _factory.Syntax; _factory.Syntax = (mg.ReceiverOpt ?? mg).Syntax; - var receiver = (!method.RequiresInstanceReceiver && !node.IsExtensionMethod) ? _factory.Type(method.ContainingType) : VisitExpression(mg.ReceiverOpt)!; + var receiver = (!method.RequiresInstanceReceiver && !node.IsExtensionMethod && !method.IsAbstract) ? _factory.Type(method.ContainingType) : VisitExpression(mg.ReceiverOpt)!; _factory.Syntax = oldSyntax; return node.Update(receiver, method, node.IsExtensionMethod, node.Type); } diff --git a/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs index 2aa13473a3b72..e853e3c1ba206 100644 --- a/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs @@ -512,7 +512,7 @@ public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationE public override BoundNode VisitFunctionPointerLoad(BoundFunctionPointerLoad node) { - return node.Update(VisitMethodSymbol(node.TargetMethod), VisitType(node.Type)); + return node.Update(VisitMethodSymbol(node.TargetMethod), VisitType(node.ConstrainedToTypeOpt), VisitType(node.Type)); } public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalAccess node) diff --git a/src/Compilers/CSharp/Portable/Symbols/OverriddenOrHiddenMembersHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/OverriddenOrHiddenMembersHelpers.cs index 799174c54f6a7..2597633084c75 100644 --- a/src/Compilers/CSharp/Portable/Symbols/OverriddenOrHiddenMembersHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/OverriddenOrHiddenMembersHelpers.cs @@ -1019,7 +1019,7 @@ internal static MethodSymbol GetFirstRuntimeOverriddenMethodIgnoringNewSlot(this const bool ignoreInterfaceImplementationChanges = true; wasAmbiguous = false; - if (!method.IsMetadataVirtual(ignoreInterfaceImplementationChanges)) + if (!method.IsMetadataVirtual(ignoreInterfaceImplementationChanges) || method.IsStatic) { return null; } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index eba76778f59c9..975815d2a72d5 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -5662,6 +5662,33 @@ .locals init (string V_0) IL_0009: ldloc.0 IL_000a: ret } +"); + + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02()", +@" +{ + // Code size 12 (0xc) + .maxstack 0 + IL_0000: constrained. ""T"" + IL_0006: call ""void I1.M01()"" + IL_000b: ret +} +"); + + verifier.VerifyIL("Test.M03()", +@" +{ + // Code size 6 (0x6) + .maxstack 1 + IL_0000: ldstr ""M01"" + IL_0005: ret +} "); var tree = compilation1.SyntaxTrees.Single(); @@ -5923,7 +5950,7 @@ static T M02(T x) where T : U where U : I1 verifier.VerifyIL("Test.M02(T)", @" { - // Code size 22 (0x16) + // Code size 21 (0x15) .maxstack 2 .locals init (T V_0) IL_0000: nop @@ -5932,17 +5959,16 @@ .locals init (T V_0) IL_0008: call ""T I1." + metadataName + @"(T)"" IL_000d: dup IL_000e: starg.s V_0 - IL_0010: dup - IL_0011: stloc.0 - IL_0012: br.s IL_0014 - IL_0014: ldloc.0 - IL_0015: ret + IL_0010: stloc.0 + IL_0011: br.s IL_0013 + IL_0013: ldloc.0 + IL_0014: ret } "); verifier.VerifyIL("Test.M03(T?)", @" { - // Code size 55 (0x37) + // Code size 54 (0x36) .maxstack 2 .locals init (T? V_0, T? V_1, @@ -5964,11 +5990,10 @@ .locals init (T? V_0, IL_0029: newobj ""T?..ctor(T)"" IL_002e: dup IL_002f: starg.s V_0 - IL_0031: dup - IL_0032: stloc.2 - IL_0033: br.s IL_0035 - IL_0035: ldloc.2 - IL_0036: ret + IL_0031: stloc.2 + IL_0032: br.s IL_0034 + IL_0034: ldloc.2 + IL_0035: ret } "); break; @@ -5978,7 +6003,7 @@ .locals init (T? V_0, verifier.VerifyIL("Test.M02(T)", @" { - // Code size 22 (0x16) + // Code size 21 (0x15) .maxstack 2 .locals init (T V_0) IL_0000: nop @@ -5987,17 +6012,16 @@ .locals init (T V_0) IL_0003: constrained. ""T"" IL_0009: call ""T I1." + metadataName + @"(T)"" IL_000e: starg.s V_0 - IL_0010: dup - IL_0011: stloc.0 - IL_0012: br.s IL_0014 - IL_0014: ldloc.0 - IL_0015: ret + IL_0010: stloc.0 + IL_0011: br.s IL_0013 + IL_0013: ldloc.0 + IL_0014: ret } "); verifier.VerifyIL("Test.M03(T?)", @" { - // Code size 55 (0x37) + // Code size 54 (0x36) .maxstack 2 .locals init (T? V_0, T? V_1, @@ -6019,11 +6043,10 @@ .locals init (T? V_0, IL_0025: call ""T I1." + metadataName + @"(T)"" IL_002a: newobj ""T?..ctor(T)"" IL_002f: starg.s V_0 - IL_0031: dup - IL_0032: stloc.2 - IL_0033: br.s IL_0035 - IL_0035: ldloc.2 - IL_0036: ret + IL_0031: stloc.2 + IL_0032: br.s IL_0034 + IL_0034: ldloc.2 + IL_0035: ret } "); break; @@ -6077,6 +6100,139 @@ .locals init (T? V_0, break; } + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + switch ((prefixOp, postfixOp)) + { + case ("++", ""): + case ("--", ""): + verifier.VerifyIL("Test.M02(T)", +@" +{ + // Code size 16 (0x10) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: constrained. ""T"" + IL_0007: call ""T I1." + metadataName + @"(T)"" + IL_000c: dup + IL_000d: starg.s V_0 + IL_000f: ret +} +"); + verifier.VerifyIL("Test.M03(T?)", +@" +{ + // Code size 49 (0x31) + .maxstack 2 + .locals init (T? V_0, + T? V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""readonly bool T?.HasValue.get"" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_1 + IL_000d: initobj ""T?"" + IL_0013: ldloc.1 + IL_0014: br.s IL_002d + IL_0016: ldloca.s V_0 + IL_0018: call ""readonly T T?.GetValueOrDefault()"" + IL_001d: constrained. ""T"" + IL_0023: call ""T I1." + metadataName + @"(T)"" + IL_0028: newobj ""T?..ctor(T)"" + IL_002d: dup + IL_002e: starg.s V_0 + IL_0030: ret +} +"); + break; + + case ("", "++"): + case ("", "--"): + verifier.VerifyIL("Test.M02(T)", +@" +{ + // Code size 16 (0x10) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: dup + IL_0002: constrained. ""T"" + IL_0008: call ""T I1." + metadataName + @"(T)"" + IL_000d: starg.s V_0 + IL_000f: ret +} +"); + verifier.VerifyIL("Test.M03(T?)", +@" +{ + // Code size 49 (0x31) + .maxstack 2 + .locals init (T? V_0, + T? V_1) + IL_0000: ldarg.0 + IL_0001: dup + IL_0002: stloc.0 + IL_0003: ldloca.s V_0 + IL_0005: call ""readonly bool T?.HasValue.get"" + IL_000a: brtrue.s IL_0017 + IL_000c: ldloca.s V_1 + IL_000e: initobj ""T?"" + IL_0014: ldloc.1 + IL_0015: br.s IL_002e + IL_0017: ldloca.s V_0 + IL_0019: call ""readonly T T?.GetValueOrDefault()"" + IL_001e: constrained. ""T"" + IL_0024: call ""T I1." + metadataName + @"(T)"" + IL_0029: newobj ""T?..ctor(T)"" + IL_002e: starg.s V_0 + IL_0030: ret +} +"); + break; + + default: + verifier.VerifyIL("Test.M02(T)", +@" +{ + // Code size 13 (0xd) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: constrained. ""T"" + IL_0007: call ""T I1." + metadataName + @"(T)"" + IL_000c: ret +} +"); + verifier.VerifyIL("Test.M03(T?)", +@" +{ + // Code size 45 (0x2d) + .maxstack 1 + .locals init (T? V_0, + T? V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""readonly bool T?.HasValue.get"" + IL_0009: brtrue.s IL_0015 + IL_000b: ldloca.s V_1 + IL_000d: initobj ""T?"" + IL_0013: ldloc.1 + IL_0014: ret + IL_0015: ldloca.s V_0 + IL_0017: call ""readonly T T?.GetValueOrDefault()"" + IL_001c: constrained. ""T"" + IL_0022: call ""T I1." + metadataName + @"(T)"" + IL_0027: newobj ""T?..ctor(T)"" + IL_002c: ret +} +"); + break; + } + var tree = compilation1.SyntaxTrees.Single(); var model = compilation1.GetSemanticModel(tree); var node = postfixOp != "" ? (ExpressionSyntax)tree.GetRoot().DescendantNodes().OfType().First() : tree.GetRoot().DescendantNodes().OfType().First(); @@ -6314,6 +6470,25 @@ .maxstack 1 IL_000f: br.s IL_0011 IL_0011: ret } +"); + + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02(T)", +@" +{ + // Code size 14 (0xe) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: constrained. ""T"" + IL_0007: call ""bool I1.op_True(T)"" + IL_000c: pop + IL_000d: ret +} "); var tree = compilation1.SyntaxTrees.Single(); @@ -6982,6 +7157,111 @@ .locals init (T? V_0) IL_0021: pop IL_0022: ret } +"); + + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + if (!isShiftOperator) + { + verifier.VerifyIL("Test.M02(T)", +@" +{ + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldc.i4.1 + IL_0001: ldarg.0 + IL_0002: constrained. ""T"" + IL_0008: call ""T I1." + metadataName + @"(int, T)"" + IL_000d: pop + IL_000e: ret +} +"); + verifier.VerifyIL("Test.M04(T?)", +@" +{ + // Code size 32 (0x20) + .maxstack 2 + .locals init (T? V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""readonly bool T?.HasValue.get"" + IL_0009: brfalse.s IL_001f + IL_000b: ldc.i4.1 + IL_000c: ldloca.s V_0 + IL_000e: call ""readonly T T?.GetValueOrDefault()"" + IL_0013: constrained. ""T"" + IL_0019: call ""T I1." + metadataName + @"(int, T)"" + IL_001e: pop + IL_001f: ret +} +"); + verifier.VerifyIL("Test.M06(I1, T)", +@" +{ + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: constrained. ""T"" + IL_0008: call ""T I1." + metadataName + @"(I1, T)"" + IL_000d: pop + IL_000e: ret +} +"); + + verifier.VerifyIL("Test.M07(T, I1)", +@" +{ + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: constrained. ""T"" + IL_0008: call ""T I1." + metadataName + @"(T, I1)"" + IL_000d: pop + IL_000e: ret +} +"); + } + + verifier.VerifyIL("Test.M03(T)", +@" +{ + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: constrained. ""T"" + IL_0008: call ""T I1." + metadataName + @"(T, int)"" + IL_000d: pop + IL_000e: ret +} +"); + + verifier.VerifyIL("Test.M05(T?)", +@" +{ + // Code size 32 (0x20) + .maxstack 2 + .locals init (T? V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""readonly bool T?.HasValue.get"" + IL_0009: brfalse.s IL_001f + IL_000b: ldloca.s V_0 + IL_000d: call ""readonly T T?.GetValueOrDefault()"" + IL_0012: ldc.i4.1 + IL_0013: constrained. ""T"" + IL_0019: call ""T I1." + metadataName + @"(T, int)"" + IL_001e: pop + IL_001f: ret +} "); var tree = compilation1.SyntaxTrees.Single(); @@ -7118,6 +7398,66 @@ .locals init (T? V_0, IL_0042: br.s IL_0044 IL_0044: ret } +"); + + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreAppAndCSharp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + verifier.VerifyIL("Test.M03(T, T)", +@" +{ + // Code size 31 (0x1f) + .maxstack 2 + .locals init (T V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: constrained. ""T"" + IL_0009: call ""bool I1." + unaryMetadataName + @"(T)"" + IL_000e: brtrue.s IL_001e + IL_0010: ldloc.0 + IL_0011: ldarg.1 + IL_0012: constrained. ""T"" + IL_0018: call ""T I1." + binaryMetadataName + @"(T, T)"" + IL_001d: pop + IL_001e: ret +} +"); + verifier.VerifyIL("Test.M04(T?, T?)", +@" +{ + // Code size 64 (0x40) + .maxstack 2 + .locals init (T? V_0, + T? V_1, + T? V_2) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: constrained. ""T"" + IL_0009: call ""bool I2." + unaryMetadataName + @"(T?)"" + IL_000e: brtrue.s IL_003f + IL_0010: ldloc.0 + IL_0011: stloc.1 + IL_0012: ldarg.1 + IL_0013: stloc.2 + IL_0014: ldloca.s V_1 + IL_0016: call ""readonly bool T?.HasValue.get"" + IL_001b: ldloca.s V_2 + IL_001d: call ""readonly bool T?.HasValue.get"" + IL_0022: and + IL_0023: brfalse.s IL_003f + IL_0025: ldloca.s V_1 + IL_0027: call ""readonly T T?.GetValueOrDefault()"" + IL_002c: ldloca.s V_2 + IL_002e: call ""readonly T T?.GetValueOrDefault()"" + IL_0033: constrained. ""T"" + IL_0039: call ""T I2." + binaryMetadataName + @"(T, T)"" + IL_003e: pop + IL_003f: ret +} "); var tree = compilation1.SyntaxTrees.Single(); @@ -7274,35 +7614,138 @@ .locals init (I1 V_0) break; } - var tree = compilation1.SyntaxTrees.Single(); - var model = compilation1.GetSemanticModel(tree); - var node1 = tree.GetRoot().DescendantNodes().OfType().Where(n => n.ToString() == "x " + op + op + " y").First(); + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreAppAndCSharp); - Assert.Equal("x " + op + op + " y", node1.ToString()); + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); - VerifyOperationTreeForNode(compilation1, model, node1, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" constraint is important for this operator, but it is not -// reflected in the IOperation tree. Should we change the shape of the tree in order -// to expose this information? + switch (binaryIsAbstract, unaryIsAbstract) + { + case (true, false): + verifier.VerifyIL("Test.M03(T, T)", @" -IBinaryOperation (BinaryOperatorKind." + opKind + @") (OperatorMethod: I1 I1." + binaryMetadataName + @"(I1 a, I1 x)) (OperationKind.Binary, Type: I1) (Syntax: 'x " + op + op + @" y') - Left: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: I1, IsImplicit) (Syntax: 'x') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T) (Syntax: 'x') - Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: I1, IsImplicit) (Syntax: 'y') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: T) (Syntax: 'y') +{ + // Code size 35 (0x23) + .maxstack 2 + .locals init (I1 V_0) + IL_0000: ldarg.0 + IL_0001: box ""T"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""bool I1." + unaryMetadataName + @"(I1)"" + IL_000d: brtrue.s IL_0022 + IL_000f: ldloc.0 + IL_0010: ldarg.1 + IL_0011: box ""T"" + IL_0016: constrained. ""T"" + IL_001c: call ""I1 I1." + binaryMetadataName + @"(I1, I1)"" + IL_0021: pop + IL_0022: ret +} "); - } - } + verifier.VerifyIL("Test.M04(T?, T?)", +@" +{ + // Code size 35 (0x23) + .maxstack 2 + .locals init (I1 V_0) + IL_0000: ldarg.0 + IL_0001: box ""T?"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""bool I1." + unaryMetadataName + @"(I1)"" + IL_000d: brtrue.s IL_0022 + IL_000f: ldloc.0 + IL_0010: ldarg.1 + IL_0011: box ""T?"" + IL_0016: constrained. ""T"" + IL_001c: call ""I1 I1." + binaryMetadataName + @"(I1, I1)"" + IL_0021: pop + IL_0022: ret +} +"); + break; - [Theory] - [InlineData("+", "op_Addition", "Add")] - [InlineData("-", "op_Subtraction", "Subtract")] + case (false, true): + verifier.VerifyIL("Test.M03(T, T)", +@" +{ + // Code size 35 (0x23) + .maxstack 2 + .locals init (I1 V_0) + IL_0000: ldarg.0 + IL_0001: box ""T"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: constrained. ""T"" + IL_000e: call ""bool I1." + unaryMetadataName + @"(I1)"" + IL_0013: brtrue.s IL_0022 + IL_0015: ldloc.0 + IL_0016: ldarg.1 + IL_0017: box ""T"" + IL_001c: call ""I1 I1." + binaryMetadataName + @"(I1, I1)"" + IL_0021: pop + IL_0022: ret +} +"); + verifier.VerifyIL("Test.M04(T?, T?)", +@" +{ + // Code size 35 (0x23) + .maxstack 2 + .locals init (I1 V_0) + IL_0000: ldarg.0 + IL_0001: box ""T?"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: constrained. ""T"" + IL_000e: call ""bool I1." + unaryMetadataName + @"(I1)"" + IL_0013: brtrue.s IL_0022 + IL_0015: ldloc.0 + IL_0016: ldarg.1 + IL_0017: box ""T?"" + IL_001c: call ""I1 I1." + binaryMetadataName + @"(I1, I1)"" + IL_0021: pop + IL_0022: ret +} +"); + break; + + default: + Assert.True(false); + break; + } + + var tree = compilation1.SyntaxTrees.Single(); + var model = compilation1.GetSemanticModel(tree); + var node1 = tree.GetRoot().DescendantNodes().OfType().Where(n => n.ToString() == "x " + op + op + " y").First(); + + Assert.Equal("x " + op + op + " y", node1.ToString()); + + VerifyOperationTreeForNode(compilation1, model, node1, +// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" constraint is important for this operator, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? +@" +IBinaryOperation (BinaryOperatorKind." + opKind + @") (OperatorMethod: I1 I1." + binaryMetadataName + @"(I1 a, I1 x)) (OperationKind.Binary, Type: I1) (Syntax: 'x " + op + op + @" y') + Left: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: I1, IsImplicit) (Syntax: 'x') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T) (Syntax: 'x') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: I1, IsImplicit) (Syntax: 'y') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: T) (Syntax: 'y') +"); + } + } + + [Theory] + [InlineData("+", "op_Addition", "Add")] + [InlineData("-", "op_Subtraction", "Subtract")] [InlineData("*", "op_Multiply", "Multiply")] [InlineData("/", "op_Division", "Divide")] [InlineData("%", "op_Modulus", "Remainder")] @@ -7501,6 +7944,130 @@ .locals init (T? V_0, IL_002f: starg.s V_0 IL_0031: ret } +"); + + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + if (!isShiftOperator) + { + verifier.VerifyIL("Test.M02(int, T)", +@" +{ + // Code size 16 (0x10) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: constrained. ""T"" + IL_0008: call ""int I1." + metadataName + @"(int, T)"" + IL_000d: starg.s V_0 + IL_000f: ret +} +"); + verifier.VerifyIL("Test.M04(int?, T?)", +@" +{ + // Code size 65 (0x41) + .maxstack 2 + .locals init (int? V_0, + T? V_1, + int? V_2) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldarg.1 + IL_0003: stloc.1 + IL_0004: ldloca.s V_0 + IL_0006: call ""readonly bool int?.HasValue.get"" + IL_000b: ldloca.s V_1 + IL_000d: call ""readonly bool T?.HasValue.get"" + IL_0012: and + IL_0013: brtrue.s IL_0020 + IL_0015: ldloca.s V_2 + IL_0017: initobj ""int?"" + IL_001d: ldloc.2 + IL_001e: br.s IL_003e + IL_0020: ldloca.s V_0 + IL_0022: call ""readonly int int?.GetValueOrDefault()"" + IL_0027: ldloca.s V_1 + IL_0029: call ""readonly T T?.GetValueOrDefault()"" + IL_002e: constrained. ""T"" + IL_0034: call ""int I1." + metadataName + @"(int, T)"" + IL_0039: newobj ""int?..ctor(int)"" + IL_003e: starg.s V_0 + IL_0040: ret +} +"); + verifier.VerifyIL("Test.M06(I1, T)", +@" +{ + // Code size 16 (0x10) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: constrained. ""T"" + IL_0008: call ""I1 I1." + metadataName + @"(I1, T)"" + IL_000d: starg.s V_0 + IL_000f: ret +} +"); + + verifier.VerifyIL("Test.M07(T, I1)", +@" +{ + // Code size 16 (0x10) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: constrained. ""T"" + IL_0008: call ""T I1." + metadataName + @"(T, I1)"" + IL_000d: starg.s V_0 + IL_000f: ret +} +"); + } + + verifier.VerifyIL("Test.M03(T)", +@" +{ + // Code size 16 (0x10) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: constrained. ""T"" + IL_0008: call ""T I1." + metadataName + @"(T, int)"" + IL_000d: starg.s V_0 + IL_000f: ret +} +"); + + verifier.VerifyIL("Test.M05(T?)", +@" +{ + // Code size 49 (0x31) + .maxstack 2 + .locals init (T? V_0, + T? V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""readonly bool T?.HasValue.get"" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_1 + IL_000d: initobj ""T?"" + IL_0013: ldloc.1 + IL_0014: br.s IL_002e + IL_0016: ldloca.s V_0 + IL_0018: call ""readonly T T?.GetValueOrDefault()"" + IL_001d: ldc.i4.1 + IL_001e: constrained. ""T"" + IL_0024: call ""T I1." + metadataName + @"(T, int)"" + IL_0029: newobj ""T?..ctor(T)"" + IL_002e: starg.s V_0 + IL_0030: ret +} "); var tree = compilation1.SyntaxTrees.Single(); @@ -8311,6 +8878,24 @@ .maxstack 1 IL_000c: pop IL_000d: ret } +"); + + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02()", +@" +{ + // Code size 13 (0xd) + .maxstack 1 + IL_0000: constrained. ""T"" + IL_0006: call ""int I1.P01.get"" + IL_000b: pop + IL_000c: ret +} "); var tree = compilation1.SyntaxTrees.Single(); @@ -8369,6 +8954,24 @@ .maxstack 1 IL_000d: nop IL_000e: ret } +"); + + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02()", +@" +{ + // Code size 13 (0xd) + .maxstack 1 + IL_0000: ldc.i4.1 + IL_0001: constrained. ""T"" + IL_0007: call ""void I1.P01.set"" + IL_000c: ret +} "); var tree = compilation1.SyntaxTrees.Single(); @@ -8450,6 +9053,37 @@ .locals init (string V_0) IL_0009: ldloc.0 IL_000a: ret } +"); + + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02()", +@" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: constrained. ""T"" + IL_0006: call ""int I1.P01.get"" + IL_000b: ldc.i4.1 + IL_000c: add + IL_000d: constrained. ""T"" + IL_0013: call ""void I1.P01.set"" + IL_0018: ret +} +"); + + verifier.VerifyIL("Test.M03()", +@" +{ + // Code size 6 (0x6) + .maxstack 1 + IL_0000: ldstr ""P01"" + IL_0005: ret +} "); var tree = compilation1.SyntaxTrees.Single(); @@ -9095,6 +9729,37 @@ .locals init (string V_0) IL_0009: ldloc.0 IL_000a: ret } +"); + + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02()", +@" +{ + // Code size 25 (0x19) + .maxstack 1 + IL_0000: ldnull + IL_0001: constrained. ""T"" + IL_0007: call ""void I1.E01.add"" + IL_000c: ldnull + IL_000d: constrained. ""T"" + IL_0013: call ""void I1.E01.remove"" + IL_0018: ret +} +"); + + verifier.VerifyIL("Test.M03()", +@" +{ + // Code size 6 (0x6) + .maxstack 1 + IL_0000: ldstr ""E01"" + IL_0005: ret +} "); var tree = compilation1.SyntaxTrees.Single(); @@ -9502,5 +10167,784 @@ .maxstack 3 } "); } + + [Fact] + public void ConsumeAbstractStaticMethod_ConversionToDelegate_01() + { + var source1 = +@" +interface I1 +{ + abstract static void M01(); + + static void M02() + { + _ = (System.Action)M01; + _ = (System.Action)M04; + } + + void M03() + { + _ = (System.Action)this.M01; + _ = (System.Action)this.M04; + } + + static void M04() {} + + protected abstract static void M05(); +} + +class Test +{ + static void MT1(I1 x) + { + _ = (System.Action)I1.M01; + _ = (System.Action)x.M01; + _ = (System.Action)I1.M04; + _ = (System.Action)x.M04; + } + + static void MT2() where T : I1 + { + _ = (System.Action)T.M03; + _ = (System.Action)T.M04; + _ = (System.Action)T.M00; + _ = (System.Action)T.M05; + + _ = (System.Linq.Expressions.Expression)(() => ((System.Action)T.M01).ToString()); + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (8,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // _ = (System.Action)M01; + Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "(System.Action)M01").WithLocation(8, 13), + // (14,28): error CS0176: Member 'I1.M01()' cannot be accessed with an instance reference; qualify it with a type name instead + // _ = (System.Action)this.M01; + Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.M01").WithArguments("I1.M01()").WithLocation(14, 28), + // (15,28): error CS0176: Member 'I1.M04()' cannot be accessed with an instance reference; qualify it with a type name instead + // _ = (System.Action)this.M04; + Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.M04").WithArguments("I1.M04()").WithLocation(15, 28), + // (27,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // _ = (System.Action)I1.M01; + Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "(System.Action)I1.M01").WithLocation(27, 13), + // (28,28): error CS0176: Member 'I1.M01()' cannot be accessed with an instance reference; qualify it with a type name instead + // _ = (System.Action)x.M01; + Diagnostic(ErrorCode.ERR_ObjectProhibited, "x.M01").WithArguments("I1.M01()").WithLocation(28, 28), + // (30,28): error CS0176: Member 'I1.M04()' cannot be accessed with an instance reference; qualify it with a type name instead + // _ = (System.Action)x.M04; + Diagnostic(ErrorCode.ERR_ObjectProhibited, "x.M04").WithArguments("I1.M04()").WithLocation(30, 28), + // (35,28): error CS0119: 'T' is a type parameter, which is not valid in the given context + // _ = (System.Action)T.M03; + Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type parameter").WithLocation(35, 28), + // (36,28): error CS0119: 'T' is a type parameter, which is not valid in the given context + // _ = (System.Action)T.M04; + Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type parameter").WithLocation(36, 28), + // (37,28): error CS0119: 'T' is a type parameter, which is not valid in the given context + // _ = (System.Action)T.M00; + Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type parameter").WithLocation(37, 28), + // (38,30): error CS0122: 'I1.M05()' is inaccessible due to its protection level + // _ = (System.Action)T.M05; + Diagnostic(ErrorCode.ERR_BadAccess, "M05").WithArguments("I1.M05()").WithLocation(38, 30), + // (40,87): error CS9108: An expression tree may not contain an access of static abstract interface member + // _ = (System.Linq.Expressions.Expression)(() => ((System.Action)T.M01).ToString()); + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, "T.M01").WithLocation(40, 87) + ); + } + + [Fact] + public void ConsumeAbstractStaticMethod_ConversionToDelegate_03() + { + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} + +class Test +{ + static System.Action M02() where T : U where U : I1 + { + return T.M01; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02()", +@" +{ + // Code size 24 (0x18) + .maxstack 2 + .locals init (System.Action V_0) + IL_0000: nop + IL_0001: ldnull + IL_0002: constrained. ""T"" + IL_0008: ldftn ""void I1.M01()"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stloc.0 + IL_0014: br.s IL_0016 + IL_0016: ldloc.0 + IL_0017: ret +} +"); + + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02()", +@" +{ + // Code size 19 (0x13) + .maxstack 2 + IL_0000: ldnull + IL_0001: constrained. ""T"" + IL_0007: ldftn ""void I1.M01()"" + IL_000d: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0012: ret +} +"); + + var tree = compilation1.SyntaxTrees.Single(); + var model = compilation1.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().First(); + + Assert.Equal("T.M01", node.ToString()); + VerifyOperationTreeForNode(compilation1, model, node, +// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" qualifier is important for this invocation, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? +@" +IMethodReferenceOperation: void I1.M01() (IsVirtual) (Static) (OperationKind.MethodReference, Type: null) (Syntax: 'T.M01') + Instance Receiver: + null +"); + } + + [Fact] + public void ConsumeAbstractStaticMethod_ConversionToDelegate_04() + { + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} +"; + var source2 = +@" +class Test +{ + static void M02() where T : I1 + { + _ = (System.Action)T.M01; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (6,13): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // _ = (System.Action)T.M01; + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "(System.Action)T.M01").WithLocation(6, 13) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation3.VerifyDiagnostics( + // (12,26): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static void M01(); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(12, 26) + ); + } + + [Fact] + public void ConsumeAbstractStaticMethod_ConversionToDelegate_06() + { + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} +"; + var source2 = +@" +class Test +{ + static void M02() where T : I1 + { + _ = (System.Action)T.M01; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (6,28): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // _ = (System.Action)T.M01; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "T").WithArguments("static abstract members in interfaces").WithLocation(6, 28) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation3.VerifyDiagnostics( + // (6,28): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // _ = (System.Action)T.M01; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "T").WithArguments("static abstract members in interfaces").WithLocation(6, 28), + // (12,26): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static void M01(); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "9.0", "preview").WithLocation(12, 26) + ); + } + + [Fact] + public void ConsumeAbstractStaticMethod_DelegateCreation_01() + { + var source1 = +@" +interface I1 +{ + abstract static void M01(); + + static void M02() + { + _ = new System.Action(M01); + _ = new System.Action(M04); + } + + void M03() + { + _ = new System.Action(this.M01); + _ = new System.Action(this.M04); + } + + static void M04() {} + + protected abstract static void M05(); +} + +class Test +{ + static void MT1(I1 x) + { + _ = new System.Action(I1.M01); + _ = new System.Action(x.M01); + _ = new System.Action(I1.M04); + _ = new System.Action(x.M04); + } + + static void MT2() where T : I1 + { + _ = new System.Action(T.M03); + _ = new System.Action(T.M04); + _ = new System.Action(T.M00); + _ = new System.Action(T.M05); + + _ = (System.Linq.Expressions.Expression)(() => new System.Action(T.M01).ToString()); + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (8,31): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // _ = new System.Action(M01); + Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "M01").WithLocation(8, 31), + // (14,31): error CS0176: Member 'I1.M01()' cannot be accessed with an instance reference; qualify it with a type name instead + // _ = new System.Action(this.M01); + Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.M01").WithArguments("I1.M01()").WithLocation(14, 31), + // (15,31): error CS0176: Member 'I1.M04()' cannot be accessed with an instance reference; qualify it with a type name instead + // _ = new System.Action(this.M04); + Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.M04").WithArguments("I1.M04()").WithLocation(15, 31), + // (27,31): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // _ = new System.Action(I1.M01); + Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "I1.M01").WithLocation(27, 31), + // (28,31): error CS0176: Member 'I1.M01()' cannot be accessed with an instance reference; qualify it with a type name instead + // _ = new System.Action(x.M01); + Diagnostic(ErrorCode.ERR_ObjectProhibited, "x.M01").WithArguments("I1.M01()").WithLocation(28, 31), + // (30,31): error CS0176: Member 'I1.M04()' cannot be accessed with an instance reference; qualify it with a type name instead + // _ = new System.Action(x.M04); + Diagnostic(ErrorCode.ERR_ObjectProhibited, "x.M04").WithArguments("I1.M04()").WithLocation(30, 31), + // (35,31): error CS0119: 'T' is a type parameter, which is not valid in the given context + // _ = new System.Action(T.M03); + Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type parameter").WithLocation(35, 31), + // (36,31): error CS0119: 'T' is a type parameter, which is not valid in the given context + // _ = new System.Action(T.M04); + Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type parameter").WithLocation(36, 31), + // (37,31): error CS0119: 'T' is a type parameter, which is not valid in the given context + // _ = new System.Action(T.M00); + Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type parameter").WithLocation(37, 31), + // (38,33): error CS0122: 'I1.M05()' is inaccessible due to its protection level + // _ = new System.Action(T.M05); + Diagnostic(ErrorCode.ERR_BadAccess, "M05").WithArguments("I1.M05()").WithLocation(38, 33), + // (40,89): error CS9108: An expression tree may not contain an access of static abstract interface member + // _ = (System.Linq.Expressions.Expression)(() => new System.Action(T.M01).ToString()); + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, "T.M01").WithLocation(40, 89) + ); + } + + [Fact] + public void ConsumeAbstractStaticMethod_DelegateCreation_03() + { + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} + +class Test +{ + static System.Action M02() where T : U where U : I1 + { + return new System.Action(T.M01); + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02()", +@" +{ + // Code size 24 (0x18) + .maxstack 2 + .locals init (System.Action V_0) + IL_0000: nop + IL_0001: ldnull + IL_0002: constrained. ""T"" + IL_0008: ldftn ""void I1.M01()"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stloc.0 + IL_0014: br.s IL_0016 + IL_0016: ldloc.0 + IL_0017: ret +} +"); + + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02()", +@" +{ + // Code size 19 (0x13) + .maxstack 2 + IL_0000: ldnull + IL_0001: constrained. ""T"" + IL_0007: ldftn ""void I1.M01()"" + IL_000d: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0012: ret +} +"); + + var tree = compilation1.SyntaxTrees.Single(); + var model = compilation1.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().First(); + + Assert.Equal("T.M01", node.ToString()); + VerifyOperationTreeForNode(compilation1, model, node, +// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" qualifier is important for this invocation, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? +@" +IMethodReferenceOperation: void I1.M01() (IsVirtual) (Static) (OperationKind.MethodReference, Type: null) (Syntax: 'T.M01') + Instance Receiver: + null +"); + } + + [Fact] + public void ConsumeAbstractStaticMethod_DelegateCreation_04() + { + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} +"; + var source2 = +@" +class Test +{ + static void M02() where T : I1 + { + _ = new System.Action(T.M01); + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (6,31): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // _ = new System.Action(T.M01); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "T.M01").WithLocation(6, 31) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation3.VerifyDiagnostics( + // (12,26): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static void M01(); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(12, 26) + ); + } + + [Fact] + public void ConsumeAbstractStaticMethod_DelegateCreation_06() + { + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} +"; + var source2 = +@" +class Test +{ + static void M02() where T : I1 + { + _ = new System.Action(T.M01); + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (6,31): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // _ = new System.Action(T.M01); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "T").WithArguments("static abstract members in interfaces").WithLocation(6, 31) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation3.VerifyDiagnostics( + // (6,31): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // _ = new System.Action(T.M01); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "T").WithArguments("static abstract members in interfaces").WithLocation(6, 31), + // (12,26): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static void M01(); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "9.0", "preview").WithLocation(12, 26) + ); + } + + [Fact] + public void ConsumeAbstractStaticMethod_ConversionToFunctionPointer_01() + { + var source1 = +@" +unsafe interface I1 +{ + abstract static void M01(); + + static void M02() + { + _ = (delegate*)&M01; + _ = (delegate*)&M04; + } + + void M03() + { + _ = (delegate*)&this.M01; + _ = (delegate*)&this.M04; + } + + static void M04() {} + + protected abstract static void M05(); +} + +unsafe class Test +{ + static void MT1(I1 x) + { + _ = (delegate*)&I1.M01; + _ = (delegate*)&x.M01; + _ = (delegate*)&I1.M04; + _ = (delegate*)&x.M04; + } + + static void MT2() where T : I1 + { + _ = (delegate*)&T.M03; + _ = (delegate*)&T.M04; + _ = (delegate*)&T.M00; + _ = (delegate*)&T.M05; + + _ = (System.Linq.Expressions.Expression)(() => ((System.IntPtr)((delegate*)&T.M01)).ToString()); + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll.WithAllowUnsafe(true), + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (8,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // _ = (delegate*)&M01; + Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "(delegate*)&M01").WithLocation(8, 13), + // (14,13): error CS8757: No overload for 'M01' matches function pointer 'delegate*' + // _ = (delegate*)&this.M01; + Diagnostic(ErrorCode.ERR_MethFuncPtrMismatch, "(delegate*)&this.M01").WithArguments("M01", "delegate*").WithLocation(14, 13), + // (15,13): error CS8757: No overload for 'M04' matches function pointer 'delegate*' + // _ = (delegate*)&this.M04; + Diagnostic(ErrorCode.ERR_MethFuncPtrMismatch, "(delegate*)&this.M04").WithArguments("M04", "delegate*").WithLocation(15, 13), + // (27,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // _ = (delegate*)&I1.M01; + Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "(delegate*)&I1.M01").WithLocation(27, 13), + // (28,13): error CS8757: No overload for 'M01' matches function pointer 'delegate*' + // _ = (delegate*)&x.M01; + Diagnostic(ErrorCode.ERR_MethFuncPtrMismatch, "(delegate*)&x.M01").WithArguments("M01", "delegate*").WithLocation(28, 13), + // (30,13): error CS8757: No overload for 'M04' matches function pointer 'delegate*' + // _ = (delegate*)&x.M04; + Diagnostic(ErrorCode.ERR_MethFuncPtrMismatch, "(delegate*)&x.M04").WithArguments("M04", "delegate*").WithLocation(30, 13), + // (35,31): error CS0119: 'T' is a type parameter, which is not valid in the given context + // _ = (delegate*)&T.M03; + Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type parameter").WithLocation(35, 31), + // (36,31): error CS0119: 'T' is a type parameter, which is not valid in the given context + // _ = (delegate*)&T.M04; + Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type parameter").WithLocation(36, 31), + // (37,31): error CS0119: 'T' is a type parameter, which is not valid in the given context + // _ = (delegate*)&T.M00; + Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type parameter").WithLocation(37, 31), + // (38,33): error CS0122: 'I1.M05()' is inaccessible due to its protection level + // _ = (delegate*)&T.M05; + Diagnostic(ErrorCode.ERR_BadAccess, "M05").WithArguments("I1.M05()").WithLocation(38, 33), + // (40,88): error CS1944: An expression tree may not contain an unsafe pointer operation + // _ = (System.Linq.Expressions.Expression)(() => ((System.IntPtr)((delegate*)&T.M01)).ToString()); + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsPointerOp, "(delegate*)&T.M01").WithLocation(40, 88), + // (40,106): error CS8810: '&' on method groups cannot be used in expression trees + // _ = (System.Linq.Expressions.Expression)(() => ((System.IntPtr)((delegate*)&T.M01)).ToString()); + Diagnostic(ErrorCode.ERR_AddressOfMethodGroupInExpressionTree, "T.M01").WithLocation(40, 106) + ); + } + + [Fact] + public void ConsumeAbstractStaticMethod_ConversionToFunctionPointer_03() + { + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} + +unsafe class Test +{ + static delegate* M02() where T : U where U : I1 + { + return &T.M01; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll.WithAllowUnsafe(true), + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02()", +@" +{ + // Code size 18 (0x12) + .maxstack 1 + .locals init (delegate* V_0) + IL_0000: nop + IL_0001: constrained. ""T"" + IL_0007: ldftn ""void I1.M01()"" + IL_000d: stloc.0 + IL_000e: br.s IL_0010 + IL_0010: ldloc.0 + IL_0011: ret +} +"); + + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll.WithAllowUnsafe(true), + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02()", +@" +{ + // Code size 13 (0xd) + .maxstack 1 + IL_0000: constrained. ""T"" + IL_0006: ldftn ""void I1.M01()"" + IL_000c: ret +} +"); + + var tree = compilation1.SyntaxTrees.Single(); + var model = compilation1.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().First(); + + Assert.Equal("T.M01", node.ToString()); + VerifyOperationTreeForNode(compilation1, model, node, +// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" qualifier is important for this invocation, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? +@" +IMethodReferenceOperation: void I1.M01() (IsVirtual) (Static) (OperationKind.MethodReference, Type: null) (Syntax: 'T.M01') + Instance Receiver: + null +"); + } + + [Fact] + public void ConsumeAbstractStaticMethod_ConversionFunctionPointer_04() + { + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} +"; + var source2 = +@" +unsafe class Test +{ + static void M02() where T : I1 + { + _ = (delegate*)&T.M01; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll.WithAllowUnsafe(true), + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (6,13): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // _ = (delegate*)&T.M01; + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "(delegate*)&T.M01").WithLocation(6, 13) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll.WithAllowUnsafe(true), + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation3.VerifyDiagnostics( + // (12,26): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static void M01(); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(12, 26) + ); + } + + [Fact] + public void ConsumeAbstractStaticMethod_ConversionToFunctionPointer_06() + { + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} +"; + var source2 = +@" +unsafe class Test +{ + static void M02() where T : I1 + { + _ = (delegate*)&T.M01; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll.WithAllowUnsafe(true), + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (6,31): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // _ = (delegate*)&T.M01; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "T").WithArguments("static abstract members in interfaces").WithLocation(6, 31) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll.WithAllowUnsafe(true), + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation3.VerifyDiagnostics( + // (6,31): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // _ = (delegate*)&T.M01; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "T").WithArguments("static abstract members in interfaces").WithLocation(6, 31), + // (12,26): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static void M01(); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "9.0", "preview").WithLocation(12, 26) + ); + } } } From 5030478287c253be88e40b1eaf96052f359527fd Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Mon, 19 Apr 2021 23:43:14 -0700 Subject: [PATCH 054/127] Correctness fixes --- .../Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs | 5 ----- .../Core/Def/ValueTracking/ValueTrackingCommandHandler.cs | 2 -- 2 files changed, 7 deletions(-) diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs index b6f33ba165b94..efdb5ede1bf4b 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs @@ -23,8 +23,6 @@ internal class ValueTrackedTreeItemViewModel : ValueTrackingTreeItemViewModel { private bool _childrenCalculated; private readonly Solution _solution; - private readonly IClassificationFormatMap _classificationFormatMap; - private readonly ClassificationTypeMap _classificationTypeMap; private readonly IGlyphService _glyphService; private readonly IValueTrackingService _valueTrackingService; private readonly ValueTrackedItem _trackedItem; @@ -54,9 +52,6 @@ public ValueTrackedTreeItemViewModel( _glyphService = glyphService; _valueTrackingService = valueTrackingService; - _classificationFormatMap = treeViewModel.ClassificationFormatMap; - _classificationTypeMap = treeViewModel.ClassificationTypeMap; - if (children.IsDefaultOrEmpty) { // Add an empty item so the treeview has an expansion showing to calculate diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs index af6e4c97a20f8..e3788f5f20da7 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs @@ -36,7 +36,6 @@ namespace Microsoft.VisualStudio.LanguageServices.ValueTracking internal class ValueTrackingCommandHandler : ICommandHandler { private readonly IAsyncServiceProvider _serviceProvider; - private readonly SVsServiceProvider _serviceProvider1; private readonly IThreadingContext _threadingContext; private readonly ClassificationTypeMap _typeMap; private readonly IClassificationFormatMapService _classificationFormatMapService; @@ -55,7 +54,6 @@ public ValueTrackingCommandHandler( IEditorFormatMapService formatMapService) { _serviceProvider = (IAsyncServiceProvider)serviceProvider; - _serviceProvider1 = serviceProvider; _threadingContext = threadingContext; _typeMap = typeMap; _classificationFormatMapService = classificationFormatMapService; From 834951e7d4b34222f95b1c289fdcfa1d3d958e6b Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Thu, 22 Apr 2021 12:37:49 -0700 Subject: [PATCH 055/127] Track variable declarations as well as assignments --- .../ValueTracking/ValueTrackingService.cs | 50 ++++++- .../ValueTracking/CSharpValueTrackingTests.cs | 126 ++++++++++++++++++ 2 files changed, 170 insertions(+), 6 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index c0921e11f7be7..fbb198de0c79a 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -70,7 +70,7 @@ or ILocalSymbol await progressCollector.TryReportAsync(solution, location, symbol, cancellationToken).ConfigureAwait(false); } - await TrackVariableSymbolAsync(symbol, operationCollector, cancellationToken).ConfigureAwait(false); + await TrackVariableReferencesAsync(symbol, operationCollector, cancellationToken).ConfigureAwait(false); } // The selection is not on a declaration, check that the node // is on the left side of an assignment. If so, populate so we can @@ -114,8 +114,9 @@ public async Task TrackValueSourceAsync( case IPropertySymbol: case IFieldSymbol: { - // The "output" is a variable assignment, track places where it gets assigned - await TrackVariableSymbolAsync(previousTrackedItem.Symbol, operationCollector, cancellationToken).ConfigureAwait(false); + // The "output" is a variable assignment, track places where it gets assigned and defined + await TrackVariableDefinitionsAsync(previousTrackedItem.Symbol, operationCollector, cancellationToken).ConfigureAwait(false); + await TrackVariableReferencesAsync(previousTrackedItem.Symbol, operationCollector, cancellationToken).ConfigureAwait(false); } break; @@ -140,7 +141,7 @@ public async Task TrackValueSourceAsync( var isRefOrOut = parameterSymbol.IsRefOrOut(); // Always track the parameter assignments as variables, in case they are assigned anywhere in the method - await TrackVariableSymbolAsync(parameterSymbol, operationCollector, cancellationToken).ConfigureAwait(false); + await TrackVariableReferencesAsync(parameterSymbol, operationCollector, cancellationToken).ConfigureAwait(false); var trackMethod = !(isParameterForPreviousTrackedMethod || isRefOrOut); if (trackMethod) @@ -159,7 +160,44 @@ public async Task TrackValueSourceAsync( } } - private static async Task TrackVariableSymbolAsync(ISymbol symbol, OperationCollector collector, CancellationToken cancellationToken) + private static async Task TrackVariableDefinitionsAsync(ISymbol symbol, OperationCollector collector, CancellationToken cancellationToken) + { + foreach (var definitionLocation in symbol.Locations) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (definitionLocation is not { SourceTree: not null }) + { + continue; + } + + var node = definitionLocation.FindNode(cancellationToken); + var document = collector.Solution.GetRequiredDocument(node.SyntaxTree); + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + var operation = semanticModel.GetOperation(node, cancellationToken); + + var declarators = operation switch + { + IVariableDeclaratorOperation variableDeclarator => ImmutableArray.Create(variableDeclarator), + IVariableDeclarationOperation variableDeclaration => variableDeclaration.Declarators, + _ => ImmutableArray.Empty + }; + + foreach (var declarator in declarators) + { + var initializer = declarator.GetVariableInitializer(); + if (initializer is null) + { + continue; + } + + await collector.VisitAsync(initializer, cancellationToken).ConfigureAwait(false); ; + } + } + } + + private static async Task TrackVariableReferencesAsync(ISymbol symbol, OperationCollector collector, CancellationToken cancellationToken) { var findReferenceProgressCollector = new FindReferencesProgress(collector, cancellationToken: cancellationToken); await SymbolFinder.FindReferencesAsync( @@ -257,7 +295,7 @@ private static async Task TrackMethodSymbolAsync(IMethodSymbol methodSymbol, Ope continue; } - await TrackVariableSymbolAsync(outOrRefParam, collector, cancellationToken).ConfigureAwait(false); + await TrackVariableReferencesAsync(outOrRefParam, collector, cancellationToken).ConfigureAwait(false); } } diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs index b830c10f4d3c0..1239a722d4272 100644 --- a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -643,6 +643,7 @@ void M() // |> i = [|2|] [Code.cs:24] // |> if (TryConvertInt(o, out [|i|])) [Code.cs:18] // |> if (int.TryParse(o.ToString(), out [|i|])) [Code.cs:5] + // |> int i = 0 [Code.cs:15] using var workspace = TestWorkspace.CreateCSharp(code); var initialItems = await GetTrackedItemsAsync(workspace); @@ -658,6 +659,7 @@ void M() { (24, "2"), // |> i = [|2|] [Code.cs:24] (18, "i"), // |> if (TryConvertInt(o, out [|i|])) [Code.cs:18] + (15, "0"), // |> int i = 0 [Code.cs:15] }); // |> i = [|2|] [Code.cs:24] @@ -674,5 +676,129 @@ void M() await ValidateChildrenEmptyAsync(workspace, children.Single()); } + + [Fact] + public async Task TestVariableReferenceStart() + { + var code = +@" +class Test +{ + public static void M() + { + int x = GetM(); + Console.Write(x); + var y = $$x + 1; + } + + public static int GetM() + { + var x = 0; + return x; + } +}"; + + // + // |> var y = x + 1; [Code.cs:7] + // |> int x = GetM() [Code.cs:5] + // |> return x; [Code.cs:13] + // |> var x = 0; [Code.cs:12] + using var workspace = TestWorkspace.CreateCSharp(code); + + var items = await ValidateItemsAsync( + workspace, + itemInfo: new[] + { + (7, "x") // |> var y = [|x|] + 1; [Code.cs:7] + }); + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (5, "GetM()") // |> int x = [|GetM()|] [Code.cs:5] + }); + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (13, "x") // |> return [|x|]; [Code.cs:13] + }); + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (12, "0") // |> var x = [|0|]; [Code.cs:12] + }); + + await ValidateChildrenEmptyAsync(workspace, items.Single()); + } + + [Fact] + public async Task TestVariableReferenceStart2() + { + var code = +@" +class Test +{ + public static void M() + { + int x = GetM(); + Console.Write($$x); + var y = x + 1; + } + + public static int GetM() + { + var x = 0; + return x; + } +}"; + + // + // |> Console.Write(x); [Code.cs:6] + // |> int x = GetM() [Code.cs:5] + // |> return x; [Code.cs:13] + // |> var x = 0; [Code.cs:12] + using var workspace = TestWorkspace.CreateCSharp(code); + + var items = await ValidateItemsAsync( + workspace, + itemInfo: new[] + { + (6, "x") // |> Console.Write([|x|]); [Code.cs:7] + }); + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (5, "GetM()") // |> int x = [|GetM()|] [Code.cs:5] + }); + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (13, "x") // |> return [|x|]; [Code.cs:13] + }); + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (12, "0") // |> var x = [|0|]; [Code.cs:12] + }); + + await ValidateChildrenEmptyAsync(workspace, items.Single()); + } } } From 0bfe2445645d0a4dadb95cbf6fa70f81dd09759f Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Fri, 23 Apr 2021 12:58:25 -0700 Subject: [PATCH 056/127] Remove extra ; --- src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index fbb198de0c79a..85ac49dfa3082 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -192,7 +192,7 @@ private static async Task TrackVariableDefinitionsAsync(ISymbol symbol, Operatio continue; } - await collector.VisitAsync(initializer, cancellationToken).ConfigureAwait(false); ; + await collector.VisitAsync(initializer, cancellationToken).ConfigureAwait(false); } } } From 2de7aa4ee868bf3de153d296322704d5a5c9c48c Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Fri, 30 Apr 2021 09:39:38 -0700 Subject: [PATCH 057/127] Add support for implementations of interface static abstract methods in classes and structures (#52969) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In metadata an implementation should be a static, not newslot, not final, not virtual, not abstract method. Type should have a MethodImpl entry pointing to the ”body” method with a MethodDef index. The “body” must be a method declared within the type. There is no concept of an implicit interface implementation for static methods in metadata. I.e., if there is no corresponding MethodImpl entry, the method isn’t an implementation from runtime point of view. Language supports both implicit and explicit interface implementation. Relevant ECMA-335 changes PR - https://github.com/dotnet/runtime/pull/49558/ --- .../CSharp/Portable/Binder/Binder_Crefs.cs | 1 + .../CSharp/Portable/CSharpResources.resx | 8 +- .../Compiler/MethodBodySynthesizer.Lowered.cs | 2 +- .../Portable/Compiler/MethodCompiler.cs | 2 +- .../Compiler/SynthesizedMetadataCompiler.cs | 2 +- .../Emitter/Model/NamedTypeSymbolAdapter.cs | 9 + .../CSharp/Portable/Errors/ErrorCode.cs | 2 + .../AnonymousType.TypePublicSymbol.cs | 5 + .../AnonymousType.TemplateSymbol.cs | 5 + .../Portable/Symbols/ArrayTypeSymbol.cs | 5 + .../Portable/Symbols/DynamicTypeSymbol.cs | 5 + .../Portable/Symbols/ErrorTypeSymbol.cs | 5 + .../FunctionPointerTypeSymbol.cs | 5 + .../Symbols/MemberSignatureComparer.cs | 12 + .../Symbols/Metadata/PE/PENamedTypeSymbol.cs | 19 +- .../Symbols/NativeIntegerTypeSymbol.cs | 5 + .../Portable/Symbols/PointerTypeSymbol.cs | 5 + .../Retargeting/RetargetingNamedTypeSymbol.cs | 14 + .../RetargetingSymbolTranslator.cs | 1 + .../Symbols/SignatureOnlyMethodSymbol.cs | 5 +- .../Source/ExplicitInterfaceHelpers.cs | 42 +- .../Portable/Symbols/Source/ModifierUtils.cs | 32 +- .../Source/SourceMemberContainerSymbol.cs | 37 +- ...berContainerSymbol_ImplementationChecks.cs | 122 +- .../Source/SourceMemberMethodSymbol.cs | 3 +- .../Source/SourceOrdinaryMethodSymbolBase.cs | 25 +- .../Symbols/SubstitutedNamedTypeSymbol.cs | 17 + .../Portable/Symbols/SymbolExtensions.cs | 2 +- .../Synthesized/SynthesizedContainer.cs | 5 + .../SynthesizedEmbeddedAttributeSymbol.cs | 5 + ...dExplicitImplementationForwardingMethod.cs | 2 + .../SynthesizedImplementationMethod.cs | 6 +- .../Portable/Symbols/TypeParameterSymbol.cs | 5 + .../TypeSymbol.SymbolAndDiagnostics.cs | 2 +- .../CSharp/Portable/Symbols/TypeSymbol.cs | 116 +- .../Portable/xlf/CSharpResources.cs.xlf | 14 +- .../Portable/xlf/CSharpResources.de.xlf | 14 +- .../Portable/xlf/CSharpResources.es.xlf | 14 +- .../Portable/xlf/CSharpResources.fr.xlf | 14 +- .../Portable/xlf/CSharpResources.it.xlf | 14 +- .../Portable/xlf/CSharpResources.ja.xlf | 14 +- .../Portable/xlf/CSharpResources.ko.xlf | 14 +- .../Portable/xlf/CSharpResources.pl.xlf | 14 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 14 +- .../Portable/xlf/CSharpResources.ru.xlf | 14 +- .../Portable/xlf/CSharpResources.tr.xlf | 14 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 14 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 14 +- .../Test/Emit/Emit/CompilationEmitTests.cs | 2 +- .../Semantics/InheritanceBindingTests.cs | 12 +- .../Test/Semantic/Semantics/RecordTests.cs | 6 +- .../AccessorOverriddenOrHiddenMembersTests.cs | 4 +- .../DefaultInterfaceImplementationTests.cs | 46 +- .../Test/Symbol/Symbols/IndexerTests.cs | 23 +- .../Symbols/InterfaceImplementationTests.cs | 16 +- .../Symbol/Symbols/MockNamedTypeSymbol.cs | 5 + .../Symbols/OverriddenOrHiddenMembersTests.cs | 6 +- .../Symbols/Source/CustomModifierCopyTests.cs | 24 +- .../StaticAbstractMembersInInterfacesTests.cs | 1606 ++++++++++++++++- .../MetadataReader/ModuleExtensions.cs | 65 +- .../Core/Portable/PEWriter/Members.cs | 27 +- .../Symbols/Metadata/PE/PENamedTypeSymbol.vb | 16 +- .../Semantics/UserDefinedBinaryOperators.vb | 139 +- .../Symbols/EENamedTypeSymbol.cs | 5 + 64 files changed, 2509 insertions(+), 213 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs index 76b0ec981fadc..ca1a59451baf1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs @@ -757,6 +757,7 @@ private static ImmutableArray PerformCrefOverloadResolution(ArrayBuilder name: null, refKind: RefKind.None, isInitOnly: false, + isStatic: false, returnType: default, refCustomModifiers: ImmutableArray.Empty, explicitInterfaceImplementations: ImmutableArray.Empty); diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index b0b19c8d8551e..478b1b1b32cf7 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -2151,7 +2151,7 @@ If such a class is used as a base class and if the deriving class defines a dest Invalid type specified as an argument for TypeForwardedTo attribute - '{0}' does not implement interface member '{1}'. '{2}' cannot implement an interface member because it is static. + '{0}' does not implement instance interface member '{1}'. '{2}' cannot implement the interface member because it is static. '{0}' does not implement interface member '{1}'. '{2}' cannot implement an interface member because it is not public. @@ -6636,4 +6636,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ An expression tree may not contain an access of static abstract interface member + + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + + + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.Lowered.cs b/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.Lowered.cs index 7a3e6a8005652..e61efd3329cb3 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.Lowered.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.Lowered.cs @@ -363,7 +363,7 @@ internal static BoundBlock ConstructSingleInvocationMethodBody(SyntheticBoundNod argBuilder.Add(F.Parameter(param)); } - BoundExpression invocation = F.Call(useBaseReference ? (BoundExpression)F.Base(baseType: methodToInvoke.ContainingType) : F.This(), + BoundExpression invocation = F.Call(methodToInvoke.IsStatic ? null : (useBaseReference ? (BoundExpression)F.Base(baseType: methodToInvoke.ContainingType) : F.This()), methodToInvoke, argBuilder.ToImmutableAndFree()); diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs index caa0530112e29..89ce4a9598860 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs @@ -811,7 +811,7 @@ private void CompileSynthesizedExplicitImplementations(SourceMemberContainerType if (!_globalHasErrors) { var discardedDiagnostics = BindingDiagnosticBag.GetInstance(_diagnostics); - foreach (var synthesizedExplicitImpl in sourceTypeSymbol.GetSynthesizedExplicitImplementations(_cancellationToken)) + foreach (var synthesizedExplicitImpl in sourceTypeSymbol.GetSynthesizedExplicitImplementations(_cancellationToken).ForwardingMethods) { Debug.Assert(synthesizedExplicitImpl.SynthesizesLoweredBoundBody); synthesizedExplicitImpl.GenerateMethodBody(compilationState, discardedDiagnostics); diff --git a/src/Compilers/CSharp/Portable/Compiler/SynthesizedMetadataCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/SynthesizedMetadataCompiler.cs index ad17f08499d20..5509aef41b301 100644 --- a/src/Compilers/CSharp/Portable/Compiler/SynthesizedMetadataCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/SynthesizedMetadataCompiler.cs @@ -72,7 +72,7 @@ public override void VisitNamedType(NamedTypeSymbol symbol) // base type from another assembly) it is necessary for the compiler to generate explicit implementations for // some interface methods. They don't go in the symbol table, but if we are emitting metadata, then we should // generate MethodDef entries for them. - foreach (var synthesizedExplicitImpl in sourceTypeSymbol.GetSynthesizedExplicitImplementations(_cancellationToken)) + foreach (var synthesizedExplicitImpl in sourceTypeSymbol.GetSynthesizedExplicitImplementations(_cancellationToken).ForwardingMethods) { _moduleBeingBuilt.AddSynthesizedDefinition(symbol, synthesizedExplicitImpl.GetCciAdapter()); } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs index 03969d1d039e9..3acf1b74165b4 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs @@ -378,6 +378,15 @@ Cci.ITypeReference Cci.ITypeDefinition.GetBaseClass(EmitContext context) yield break; } + if (AdaptedNamedTypeSymbol is SourceMemberContainerTypeSymbol container) + { + foreach ((MethodSymbol body, MethodSymbol implemented) in container.GetSynthesizedExplicitImplementations(cancellationToken: default).MethodImpls) + { + Debug.Assert(body.ContainingType == (object)container); + yield return new Microsoft.Cci.MethodImplementation(body.GetCciAdapter(), moduleBeingBuilt.TranslateOverriddenMethodReference(implemented, (CSharpSyntaxNode)context.SyntaxNodeOpt, context.Diagnostics)); + } + } + var syntheticMethods = moduleBeingBuilt.GetSynthesizedMethods(AdaptedNamedTypeSymbol); if (syntheticMethods != null) { diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index f64f174710bb0..b8ade0c25f439 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1947,6 +1947,8 @@ internal enum ErrorCode ERR_BadAbstractShiftOperatorSignature = 9106, ERR_BadAbstractStaticMemberAccess = 9107, ERR_ExpressionTreeContainsAbstractStaticMemberAccess = 9108, + ERR_CloseUnimplementedInterfaceMemberNotStatic = 9109, + ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember = 9110, // Note: you will need to re-generate compiler code after adding warnings (eng\generate-compiler-code.cmd) } diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousType.TypePublicSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousType.TypePublicSymbol.cs index 0d646b56d8d50..5eaba6f841a56 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousType.TypePublicSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousType.TypePublicSymbol.cs @@ -347,6 +347,11 @@ public override int GetHashCode() } internal override bool HasPossibleWellKnownCloneMethod() => false; + + internal override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() + { + return SpecializedCollections.EmptyEnumerable<(MethodSymbol Body, MethodSymbol Implemented)>(); + } } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TemplateSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TemplateSymbol.cs index cec33a084a910..c91ed1cacb232 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TemplateSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TemplateSymbol.cs @@ -509,6 +509,11 @@ private SynthesizedAttributeData TrySynthesizeDebuggerDisplayAttribute() } internal override bool HasPossibleWellKnownCloneMethod() => false; + + internal override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() + { + return SpecializedCollections.EmptyEnumerable<(MethodSymbol Body, MethodSymbol Implemented)>(); + } } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs index d8b3320ff0417..46c511144648a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs @@ -481,6 +481,11 @@ protected sealed override ITypeSymbol CreateITypeSymbol(CodeAnalysis.NullableAnn internal override bool IsRecord => false; + internal sealed override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() + { + return SpecializedCollections.EmptyEnumerable<(MethodSymbol Body, MethodSymbol Implemented)>(); + } + /// /// Represents SZARRAY - zero-based one-dimensional array /// diff --git a/src/Compilers/CSharp/Portable/Symbols/DynamicTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/DynamicTypeSymbol.cs index d0cd05a8923d9..810dbb4a18dd9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/DynamicTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/DynamicTypeSymbol.cs @@ -246,5 +246,10 @@ protected sealed override ITypeSymbol CreateITypeSymbol(CodeAnalysis.NullableAnn } internal override bool IsRecord => false; + + internal override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() + { + return SpecializedCollections.EmptyEnumerable<(MethodSymbol Body, MethodSymbol Implemented)>(); + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs index 91c9fce87fca3..aefd07980a4c8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs @@ -545,6 +545,11 @@ protected sealed override ITypeSymbol CreateITypeSymbol(CodeAnalysis.NullableAnn internal sealed override bool IsRecord => false; internal sealed override bool HasPossibleWellKnownCloneMethod() => false; + + internal sealed override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() + { + return SpecializedCollections.EmptyEnumerable<(MethodSymbol Body, MethodSymbol Implemented)>(); + } } internal abstract class SubstitutedErrorTypeSymbol : ErrorTypeSymbol diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs index 816eca0df9e59..eb8fac84c88c7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs @@ -214,5 +214,10 @@ internal static bool IsCallingConventionModifier(NamedTypeSymbol modifierType) } internal override bool IsRecord => false; + + internal override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() + { + return SpecializedCollections.EmptyEnumerable<(MethodSymbol Body, MethodSymbol Implemented)>(); + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs b/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs index 42763e0cbc05e..fe73216c19a7d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs @@ -221,6 +221,18 @@ internal class MemberSignatureComparer : IEqualityComparer considerRefKindDifferences: false, typeComparison: TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes | TypeCompareKind.IgnoreNativeIntegers); + /// + /// Same as , but in addition ignores name. + /// + public static readonly MemberSignatureComparer RuntimeExplicitImplementationSignatureComparer = new MemberSignatureComparer( + considerName: false, + considerExplicitlyImplementedInterfaces: false, + considerReturnType: true, + considerTypeConstraints: false, + considerCallingConvention: true, + considerRefKindDifferences: false, + typeComparison: TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes | TypeCompareKind.IgnoreNativeIntegers); + /// /// Same as , but distinguishes between ref and out. During override resolution, /// if we find two methods that match except for ref/out, we want to prefer the one that matches, even diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs index 42675e9d2b7e0..8b936acee85f5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs @@ -1852,7 +1852,7 @@ private PooledDictionary CreateMethods(A { foreach (var methodHandle in module.GetMethodsOfTypeOrThrow(_handle)) { - if (isOrdinaryEmbeddableStruct || module.ShouldImportMethod(methodHandle, moduleSymbol.ImportOptions)) + if (isOrdinaryEmbeddableStruct || module.ShouldImportMethod(_handle, methodHandle, moduleSymbol.ImportOptions)) { var method = new PEMethodSymbol(moduleSymbol, this, methodHandle); members.Add(method); @@ -1879,8 +1879,8 @@ private void CreateProperties(Dictionary { var methods = module.GetPropertyMethodsOrThrow(propertyDef); - PEMethodSymbol getMethod = GetAccessorMethod(module, methodHandleToSymbol, methods.Getter); - PEMethodSymbol setMethod = GetAccessorMethod(module, methodHandleToSymbol, methods.Setter); + PEMethodSymbol getMethod = GetAccessorMethod(module, methodHandleToSymbol, _handle, methods.Getter); + PEMethodSymbol setMethod = GetAccessorMethod(module, methodHandleToSymbol, _handle, methods.Setter); if (((object)getMethod != null) || ((object)setMethod != null)) { @@ -1912,8 +1912,8 @@ private void CreateEvents( var methods = module.GetEventMethodsOrThrow(eventRid); // NOTE: C# ignores all other accessors (most notably, raise/fire). - PEMethodSymbol addMethod = GetAccessorMethod(module, methodHandleToSymbol, methods.Adder); - PEMethodSymbol removeMethod = GetAccessorMethod(module, methodHandleToSymbol, methods.Remover); + PEMethodSymbol addMethod = GetAccessorMethod(module, methodHandleToSymbol, _handle, methods.Adder); + PEMethodSymbol removeMethod = GetAccessorMethod(module, methodHandleToSymbol, _handle, methods.Remover); // NOTE: both accessors are required, but that will be reported separately. // Create the symbol unless both accessors are missing. @@ -1930,7 +1930,7 @@ private void CreateEvents( { } } - private PEMethodSymbol GetAccessorMethod(PEModule module, Dictionary methodHandleToSymbol, MethodDefinitionHandle methodDef) + private PEMethodSymbol GetAccessorMethod(PEModule module, Dictionary methodHandleToSymbol, TypeDefinitionHandle typeDef, MethodDefinitionHandle methodDef) { if (methodDef.IsNil) { @@ -1939,7 +1939,7 @@ private PEMethodSymbol GetAccessorMethod(PEModule module, Dictionary GetMembers(ImmutableArray m } } + internal sealed override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() + { + return SpecializedCollections.EmptyEnumerable<(MethodSymbol Body, MethodSymbol Implemented)>(); + } + /// /// Specialized PENamedTypeSymbol for types with no type parameters in /// metadata (no type parameters on this type and all containing types). diff --git a/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs index e5c2b9678c774..466e46f92bb20 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs @@ -253,6 +253,11 @@ internal static void VerifyEquality(Symbol symbolA, Symbol symbolB) Debug.Assert(symbolA.GetHashCode() == symbolB.GetHashCode()); } + internal override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() + { + return SpecializedCollections.EmptyEnumerable<(MethodSymbol Body, MethodSymbol Implemented)>(); + } + private sealed class NativeIntegerTypeMap : AbstractTypeMap { private readonly NativeIntegerTypeSymbol _type; diff --git a/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs index 8046e2384bd58..fc79a7e542464 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs @@ -310,5 +310,10 @@ protected override ITypeSymbol CreateITypeSymbol(CodeAnalysis.NullableAnnotation } internal override bool IsRecord => false; + + internal sealed override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() + { + return SpecializedCollections.EmptyEnumerable<(MethodSymbol Body, MethodSymbol Implemented)>(); + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs index d4ed3f7489b52..f65577d84e9b6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs @@ -393,5 +393,19 @@ public sealed override bool AreLocalsZeroed internal sealed override bool IsRecord => _underlyingType.IsRecord; internal sealed override bool HasPossibleWellKnownCloneMethod() => _underlyingType.HasPossibleWellKnownCloneMethod(); + + internal override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() + { + foreach ((MethodSymbol body, MethodSymbol implemented) in _underlyingType.SynthesizedInterfaceMethodImpls()) + { + var newBody = this.RetargetingTranslator.Retarget(body, MemberSignatureComparer.RetargetedExplicitImplementationComparer); + var newImplemented = this.RetargetingTranslator.Retarget(implemented, MemberSignatureComparer.RetargetedExplicitImplementationComparer); + + if (newBody is object && newImplemented is object) + { + yield return (newBody, newImplemented); + } + } + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs index 9cefa14b02a3e..b768d73d75286 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs @@ -1013,6 +1013,7 @@ IEqualityComparer retargetedMethodComparer targetParamsBuilder.ToImmutableAndFree(), method.RefKind, method.IsInitOnly, + method.IsStatic, translator.Retarget(method.ReturnTypeWithAnnotations, RetargetOptions.RetargetPrimitiveTypesByTypeCode), translator.RetargetModifiers(method.RefCustomModifiers, out modifiersHaveChanged_Ignored), ImmutableArray.Empty); diff --git a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs index 5656f9ee1110a..59a09861eb1cf 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs @@ -26,6 +26,7 @@ internal sealed class SignatureOnlyMethodSymbol : MethodSymbol private readonly ImmutableArray _parameters; private readonly RefKind _refKind; private readonly bool _isInitOnly; + private readonly bool _isStatic; private readonly TypeWithAnnotations _returnType; private readonly ImmutableArray _refCustomModifiers; private readonly ImmutableArray _explicitInterfaceImplementations; @@ -39,6 +40,7 @@ public SignatureOnlyMethodSymbol( ImmutableArray parameters, RefKind refKind, bool isInitOnly, + bool isStatic, TypeWithAnnotations returnType, ImmutableArray refCustomModifiers, ImmutableArray explicitInterfaceImplementations) @@ -48,6 +50,7 @@ public SignatureOnlyMethodSymbol( _typeParameters = typeParameters; _refKind = refKind; _isInitOnly = isInitOnly; + _isStatic = isStatic; _returnType = returnType; _refCustomModifiers = refCustomModifiers; _parameters = parameters; @@ -131,7 +134,7 @@ public SignatureOnlyMethodSymbol( public override Accessibility DeclaredAccessibility { get { throw ExceptionUtilities.Unreachable; } } - public override bool IsStatic { get { throw ExceptionUtilities.Unreachable; } } + public override bool IsStatic { get { return _isStatic; } } public override bool IsAsync { get { throw ExceptionUtilities.Unreachable; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ExplicitInterfaceHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ExplicitInterfaceHelpers.cs index cb3611b172bdf..0102595d08635 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ExplicitInterfaceHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ExplicitInterfaceHelpers.cs @@ -109,32 +109,37 @@ public static string GetMemberNameWithoutInterfaceName(string fullName) return (idx > 0) ? fullName.Substring(idx + 1) : fullName; //don't consider leading dots } +#nullable enable public static ImmutableArray SubstituteExplicitInterfaceImplementations(ImmutableArray unsubstitutedExplicitInterfaceImplementations, TypeMap map) where T : Symbol { var builder = ArrayBuilder.GetInstance(); foreach (var unsubstitutedPropertyImplemented in unsubstitutedExplicitInterfaceImplementations) { - var unsubstitutedInterfaceType = unsubstitutedPropertyImplemented.ContainingType; - Debug.Assert((object)unsubstitutedInterfaceType != null); - var explicitInterfaceType = map.SubstituteNamedType(unsubstitutedInterfaceType); - Debug.Assert((object)explicitInterfaceType != null); - var name = unsubstitutedPropertyImplemented.Name; //should already be unqualified - - T substitutedMemberImplemented = null; - foreach (var candidateMember in explicitInterfaceType.GetMembers(name)) + builder.Add(SubstituteExplicitInterfaceImplementation(unsubstitutedPropertyImplemented, map)); + } + + return builder.ToImmutableAndFree(); + } + + public static T SubstituteExplicitInterfaceImplementation(T unsubstitutedPropertyImplemented, TypeMap map) where T : Symbol + { + var unsubstitutedInterfaceType = unsubstitutedPropertyImplemented.ContainingType; + Debug.Assert((object)unsubstitutedInterfaceType != null); + var explicitInterfaceType = map.SubstituteNamedType(unsubstitutedInterfaceType); + Debug.Assert((object)explicitInterfaceType != null); + var name = unsubstitutedPropertyImplemented.Name; //should already be unqualified + + foreach (var candidateMember in explicitInterfaceType.GetMembers(name)) + { + if (candidateMember.OriginalDefinition == unsubstitutedPropertyImplemented.OriginalDefinition) { - if (candidateMember.OriginalDefinition == unsubstitutedPropertyImplemented.OriginalDefinition) - { - substitutedMemberImplemented = (T)candidateMember; - break; - } + return (T)candidateMember; } - Debug.Assert((object)substitutedMemberImplemented != null); //if it was an explicit implementation before the substitution, it should still be after - builder.Add(substitutedMemberImplemented); } - return builder.ToImmutableAndFree(); + throw ExceptionUtilities.Unreachable; } +#nullable disable internal static MethodSymbol FindExplicitlyImplementedMethod( this MethodSymbol implementingMethod, @@ -351,6 +356,11 @@ internal static void FindExplicitlyImplementedMemberVerification( // tried to implement the ambiguous interface implicitly, we would separately raise an error about // the implicit implementation methods differing by only ref/out. FindExplicitImplementationCollisions(implementingMember, implementedMember, diagnostics); + + if (implementedMember.IsStatic && !implementingMember.ContainingAssembly.RuntimeSupportsStaticAbstractMembersInInterfaces) + { + diagnostics.Add(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, implementingMember.Locations[0]); + } } /// diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs index 4ed54d4228b21..b4aa7247ea04c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs @@ -130,7 +130,7 @@ internal static void ReportDefaultInterfaceImplementationModifiers( requiredVersion = MessageID.IDS_FeatureStaticAbstractMembersInInterfaces.RequiredVersion(); if (availableVersion < requiredVersion) { - report(modifiers, reportModifiers, errorLocation, diagnostics, availableVersion, requiredVersion); + ReportUnsupportedModifiersForLanguageVersion(modifiers, reportModifiers, errorLocation, diagnostics, availableVersion, requiredVersion); } return; // below we will either ask for an earlier version of the language, or will not report anything @@ -148,26 +148,26 @@ internal static void ReportDefaultInterfaceImplementationModifiers( requiredVersion = MessageID.IDS_DefaultInterfaceImplementation.RequiredVersion(); if (availableVersion < requiredVersion) { - report(modifiers, defaultInterfaceImplementationModifiers, errorLocation, diagnostics, availableVersion, requiredVersion); + ReportUnsupportedModifiersForLanguageVersion(modifiers, defaultInterfaceImplementationModifiers, errorLocation, diagnostics, availableVersion, requiredVersion); } } } + } - static void report(DeclarationModifiers modifiers, DeclarationModifiers unsupportedModifiers, Location errorLocation, BindingDiagnosticBag diagnostics, LanguageVersion availableVersion, LanguageVersion requiredVersion) + internal static void ReportUnsupportedModifiersForLanguageVersion(DeclarationModifiers modifiers, DeclarationModifiers unsupportedModifiers, Location errorLocation, BindingDiagnosticBag diagnostics, LanguageVersion availableVersion, LanguageVersion requiredVersion) + { + DeclarationModifiers errorModifiers = modifiers & unsupportedModifiers; + var requiredVersionArgument = new CSharpRequiredLanguageVersion(requiredVersion); + var availableVersionArgument = availableVersion.ToDisplayString(); + while (errorModifiers != DeclarationModifiers.None) { - DeclarationModifiers errorModifiers = modifiers & unsupportedModifiers; - var requiredVersionArgument = new CSharpRequiredLanguageVersion(requiredVersion); - var availableVersionArgument = availableVersion.ToDisplayString(); - while (errorModifiers != DeclarationModifiers.None) - { - DeclarationModifiers oneError = errorModifiers & ~(errorModifiers - 1); - Debug.Assert(oneError != DeclarationModifiers.None); - errorModifiers = errorModifiers & ~oneError; - diagnostics.Add(ErrorCode.ERR_InvalidModifierForLanguageVersion, errorLocation, - ConvertSingleModifierToSyntaxText(oneError), - availableVersionArgument, - requiredVersionArgument); - } + DeclarationModifiers oneError = errorModifiers & ~(errorModifiers - 1); + Debug.Assert(oneError != DeclarationModifiers.None); + errorModifiers = errorModifiers & ~oneError; + diagnostics.Add(ErrorCode.ERR_InvalidModifierForLanguageVersion, errorLocation, + ConvertSingleModifierToSyntaxText(oneError), + availableVersionArgument, + requiredVersionArgument); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index bf44e77a5e295..39a5ccf99eebf 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -166,7 +166,7 @@ public bool SetNullableContext(byte? value) private static readonly Dictionary> s_emptyTypeMembers = new Dictionary>(EmptyComparer.Instance); private Dictionary>? _lazyTypeMembers; private ImmutableArray _lazyMembersFlattened; - private ImmutableArray _lazySynthesizedExplicitImplementations; + private SynthesizedExplicitImplementations? _lazySynthesizedExplicitImplementations; private int _lazyKnownCircularStruct; private LexicalSortKey _lazyLexicalSortKey = LexicalSortKey.NotInitialized; @@ -3491,6 +3491,7 @@ void addDeconstruct(SynthesizedRecordConstructor ctor, ImmutableArray.Empty, ImmutableArray.Empty); @@ -3536,6 +3537,7 @@ void addCopyCtor(bool primaryAndCopyCtorAmbiguity) )), RefKind.None, isInitOnly: false, + isStatic: false, TypeWithAnnotations.Create(compilation.GetSpecialType(SpecialType.System_Void)), ImmutableArray.Empty, ImmutableArray.Empty); @@ -3581,6 +3583,7 @@ MethodSymbol addPrintMembersMethod() RefKind.None)), RefKind.None, isInitOnly: false, + isStatic: false, returnType: TypeWithAnnotations.Create(compilation.GetSpecialType(SpecialType.System_Boolean)), refCustomModifiers: ImmutableArray.Empty, explicitInterfaceImplementations: ImmutableArray.Empty); @@ -3635,6 +3638,7 @@ void addToStringMethod(MethodSymbol printMethod) ImmutableArray.Empty, RefKind.None, isInitOnly: false, + isStatic: false, returnType: TypeWithAnnotations.Create(compilation.GetSpecialType(SpecialType.System_String)), refCustomModifiers: ImmutableArray.Empty, explicitInterfaceImplementations: ImmutableArray.Empty); @@ -3780,6 +3784,7 @@ MethodSymbol addGetHashCode(PropertySymbol equalityContract) ImmutableArray.Empty, RefKind.None, isInitOnly: false, + isStatic: false, TypeWithAnnotations.Create(compilation.GetSpecialType(SpecialType.System_Int32)), ImmutableArray.Empty, ImmutableArray.Empty); @@ -3876,6 +3881,7 @@ MethodSymbol addThisEquals(PropertySymbol equalityContract) )), RefKind.None, isInitOnly: false, + isStatic: false, TypeWithAnnotations.Create(compilation.GetSpecialType(SpecialType.System_Boolean)), ImmutableArray.Empty, ImmutableArray.Empty); @@ -4524,5 +4530,34 @@ public sealed override NamedTypeSymbol ConstructedFrom { get { return this; } } + + internal class SynthesizedExplicitImplementations + { + public static readonly SynthesizedExplicitImplementations Empty = new SynthesizedExplicitImplementations(ImmutableArray.Empty, + ImmutableArray<(MethodSymbol Body, MethodSymbol Implemented)>.Empty); + + public readonly ImmutableArray ForwardingMethods; + public readonly ImmutableArray<(MethodSymbol Body, MethodSymbol Implemented)> MethodImpls; + + private SynthesizedExplicitImplementations( + ImmutableArray forwardingMethods, + ImmutableArray<(MethodSymbol Body, MethodSymbol Implemented)> methodImpls) + { + ForwardingMethods = forwardingMethods.NullToEmpty(); + MethodImpls = methodImpls.NullToEmpty(); + } + + internal static SynthesizedExplicitImplementations Create( + ImmutableArray forwardingMethods, + ImmutableArray<(MethodSymbol Body, MethodSymbol Implemented)> methodImpls) + { + if (forwardingMethods.IsDefaultOrEmpty && methodImpls.IsDefaultOrEmpty) + { + return Empty; + } + + return new SynthesizedExplicitImplementations(forwardingMethods, methodImpls); + } + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs index 1ff781f135864..b336cca054ee6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs @@ -26,10 +26,10 @@ internal partial class SourceMemberContainerTypeSymbol /// some interface methods. They don't go in the symbol table, but if we are emitting, then we should /// generate code for them. /// - internal ImmutableArray GetSynthesizedExplicitImplementations( + internal SynthesizedExplicitImplementations GetSynthesizedExplicitImplementations( CancellationToken cancellationToken) { - if (_lazySynthesizedExplicitImplementations.IsDefault) + if (_lazySynthesizedExplicitImplementations is null) { var diagnostics = BindingDiagnosticBag.GetInstance(); try @@ -49,10 +49,10 @@ internal ImmutableArray GetSy this.CheckInterfaceVarianceSafety(diagnostics); } - if (ImmutableInterlocked.InterlockedCompareExchange( + if (Interlocked.CompareExchange( ref _lazySynthesizedExplicitImplementations, ComputeInterfaceImplementations(diagnostics, cancellationToken), - default(ImmutableArray)).IsDefault) + null) is null) { // Do not cancel from this point on. We've assigned the member, so we must add // the diagnostics. @@ -70,6 +70,21 @@ internal ImmutableArray GetSy return _lazySynthesizedExplicitImplementations; } + internal sealed override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() + { + SynthesizedExplicitImplementations synthesizedImplementations = GetSynthesizedExplicitImplementations(cancellationToken: default); + + foreach (var methodImpl in synthesizedImplementations.MethodImpls) + { + yield return methodImpl; + } + + foreach (var forwardingMethod in synthesizedImplementations.ForwardingMethods) + { + yield return (forwardingMethod.ImplementingMethod, forwardingMethod.ExplicitInterfaceImplementations.Single()); + } + } + private void CheckAbstractClassImplementations(BindingDiagnosticBag diagnostics) { NamedTypeSymbol baseType = this.BaseTypeNoUseSiteDiagnostics; @@ -92,11 +107,12 @@ private void CheckAbstractClassImplementations(BindingDiagnosticBag diagnostics) } } - private ImmutableArray ComputeInterfaceImplementations( + private SynthesizedExplicitImplementations ComputeInterfaceImplementations( BindingDiagnosticBag diagnostics, CancellationToken cancellationToken) { - var synthesizedImplementations = ArrayBuilder.GetInstance(); + var forwardingMethods = ArrayBuilder.GetInstance(); + var methodImpls = ArrayBuilder<(MethodSymbol Body, MethodSymbol Implemented)>.GetInstance(); // NOTE: We can't iterator over this collection directly, since it is not ordered. Instead we // iterate over AllInterfaces and filter out the interfaces that are not in this set. This is @@ -164,9 +180,9 @@ private ImmutableArray Comput bool wasImplementingMemberFound = (object)implementingMember != null; - if ((object)synthesizedImplementation != null) + if (synthesizedImplementation.ForwardingMethod is SynthesizedExplicitImplementationForwardingMethod forwardingMethod) { - if (synthesizedImplementation.IsVararg) + if (forwardingMethod.IsVararg) { diagnostics.Add( ErrorCode.ERR_InterfaceImplementedImplicitlyByVariadic, @@ -174,10 +190,16 @@ private ImmutableArray Comput } else { - synthesizedImplementations.Add(synthesizedImplementation); + forwardingMethods.Add(forwardingMethod); } } + if (synthesizedImplementation.MethodImpl is { } methodImpl) + { + Debug.Assert(methodImpl is { Body: not null, Implemented: not null }); + methodImpls.Add(methodImpl); + } + if (wasImplementingMemberFound && interfaceMemberKind == SymbolKind.Event) { // NOTE: unlike dev11, we're including a related location for the implementing type, because @@ -295,7 +317,7 @@ private ImmutableArray Comput // a synthesized implementation is needed that invokes the base method. // We can do so only if there are no use-site errors. - if ((object)synthesizedImplementation != null || TypeSymbol.Equals(implementingMember.ContainingType, this, TypeCompareKind.ConsiderEverything2)) + if (synthesizedImplementation.ForwardingMethod is not null || TypeSymbol.Equals(implementingMember.ContainingType, this, TypeCompareKind.ConsiderEverything2)) { UseSiteInfo useSiteInfo = interfaceMember.GetUseSiteInfo(); // Don't report a use site error with a location in another compilation. For example, @@ -314,7 +336,7 @@ private ImmutableArray Comput } } - return synthesizedImplementations.ToImmutableAndFree(); + return SynthesizedExplicitImplementations.Create(forwardingMethods.ToImmutableAndFree(), methodImpls.ToImmutableAndFree()); } protected abstract Location GetCorrespondingBaseListLocation(NamedTypeSymbol @base); @@ -1540,8 +1562,6 @@ private void CheckInterfaceUnification(BindingDiagnosticBag diagnostics) } } -#nullable disable - /// /// Though there is a method that C# considers to be an implementation of the interface method, that /// method may not be considered an implementation by the CLR. In particular, implicit implementation @@ -1552,8 +1572,12 @@ private void CheckInterfaceUnification(BindingDiagnosticBag diagnostics) /// /// Returned from FindImplementationForInterfaceMemberWithDiagnostics. /// The interface method or property that is being implemented. - /// Synthesized implementation or null if not needed. - private SynthesizedExplicitImplementationForwardingMethod SynthesizeInterfaceMemberImplementation(SymbolAndDiagnostics implementingMemberAndDiagnostics, Symbol interfaceMember) + /// + /// A synthesized forwarding method for the implementation, or information about MethodImpl entry that should be emitted, + /// or default if neither needed. + /// + private (SynthesizedExplicitImplementationForwardingMethod? ForwardingMethod, (MethodSymbol Body, MethodSymbol Implemented)? MethodImpl) + SynthesizeInterfaceMemberImplementation(SymbolAndDiagnostics implementingMemberAndDiagnostics, Symbol interfaceMember) { if (interfaceMember.DeclaredAccessibility != Accessibility.Public) { @@ -1561,14 +1585,14 @@ private SynthesizedExplicitImplementationForwardingMethod SynthesizeInterfaceMem // appropriate errors are reported elsewhere. Let's not synthesize // forwarding methods, or modify metadata virtualness of the // implementing methods. - return null; + return default; } foreach (Diagnostic diagnostic in implementingMemberAndDiagnostics.Diagnostics.Diagnostics) { if (diagnostic.Severity == DiagnosticSeverity.Error) { - return null; + return default; } } @@ -1577,7 +1601,7 @@ private SynthesizedExplicitImplementationForwardingMethod SynthesizeInterfaceMem //don't worry about properties or events - we'll catch them through their accessors if ((object)implementingMember == null || implementingMember.Kind != SymbolKind.Method) { - return null; + return default; } MethodSymbol interfaceMethod = (MethodSymbol)interfaceMember; @@ -1589,50 +1613,68 @@ private SynthesizedExplicitImplementationForwardingMethod SynthesizeInterfaceMem // implementing accessors, even for public ones. if (interfaceMethod.AssociatedSymbol?.IsEventOrPropertyWithImplementableNonPublicAccessor() == true) { - return null; + return default; } //explicit implementations are always respected by the CLR if (implementingMethod.ExplicitInterfaceImplementations.Contains(interfaceMethod, ExplicitInterfaceImplementationTargetMemberEqualityComparer.Instance)) { - return null; + return default; } - MethodSymbol implementingMethodOriginalDefinition = implementingMethod.OriginalDefinition; + if (!interfaceMethod.IsStatic) + { + MethodSymbol implementingMethodOriginalDefinition = implementingMethod.OriginalDefinition; - bool needSynthesizedImplementation = true; + bool needSynthesizedImplementation = true; - // If the implementing method is from a source file in the same module and the - // override is correct from the runtime's perspective (esp the custom modifiers - // match), then we can just twiddle the metadata virtual bit. Otherwise, we need - // to create an explicit implementation that delegates to the real implementation. - if (MemberSignatureComparer.RuntimeImplicitImplementationComparer.Equals(implementingMethod, interfaceMethod) && - IsOverrideOfPossibleImplementationUnderRuntimeRules(implementingMethod, @interfaceMethod.ContainingType)) - { - if (ReferenceEquals(this.ContainingModule, implementingMethodOriginalDefinition.ContainingModule)) + // If the implementing method is from a source file in the same module and the + // override is correct from the runtime's perspective (esp the custom modifiers + // match), then we can just twiddle the metadata virtual bit. Otherwise, we need + // to create an explicit implementation that delegates to the real implementation. + if (MemberSignatureComparer.RuntimeImplicitImplementationComparer.Equals(implementingMethod, interfaceMethod) && + IsOverrideOfPossibleImplementationUnderRuntimeRules(implementingMethod, @interfaceMethod.ContainingType)) { - SourceMemberMethodSymbol sourceImplementMethodOriginalDefinition = implementingMethodOriginalDefinition as SourceMemberMethodSymbol; - if ((object)sourceImplementMethodOriginalDefinition != null) + if (ReferenceEquals(this.ContainingModule, implementingMethodOriginalDefinition.ContainingModule)) { - sourceImplementMethodOriginalDefinition.EnsureMetadataVirtual(); + if (implementingMethodOriginalDefinition is SourceMemberMethodSymbol sourceImplementMethodOriginalDefinition) + { + sourceImplementMethodOriginalDefinition.EnsureMetadataVirtual(); + needSynthesizedImplementation = false; + } + } + else if (implementingMethod.IsMetadataVirtual(ignoreInterfaceImplementationChanges: true)) + { + // If the signatures match and the implementation method is definitely virtual, then we're set. needSynthesizedImplementation = false; } } - else if (implementingMethod.IsMetadataVirtual(ignoreInterfaceImplementationChanges: true)) + + if (!needSynthesizedImplementation) { - // If the signatures match and the implementation method is definitely virtual, then we're set. - needSynthesizedImplementation = false; + return default; } } - - if (!needSynthesizedImplementation) + else { - return null; + if (implementingMethod.ContainingType != (object)this) + { + if (implementingMethod.Equals(this.BaseTypeNoUseSiteDiagnostics?.FindImplementationForInterfaceMemberInNonInterfaceWithDiagnostics(interfaceMethod).Symbol, TypeCompareKind.CLRSignatureCompareOptions)) + { + return default; + } + } + else if (MemberSignatureComparer.RuntimeExplicitImplementationSignatureComparer.Equals(implementingMethod, interfaceMethod)) + { + return (null, (implementingMethod, interfaceMethod)); + } } - return new SynthesizedExplicitImplementationForwardingMethod(interfaceMethod, implementingMethod, this); + return (new SynthesizedExplicitImplementationForwardingMethod(interfaceMethod, implementingMethod, this), null); } +#nullable disable + /// /// The CLR will only look for an implementation of an interface method in a type that /// 1) declares that it implements that interface; or diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs index 60316e8056681..6a733d192ad94 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs @@ -436,7 +436,7 @@ internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChang // in NamedTypeSymbolAdapter.cs). return this.IsOverride ? this.RequiresExplicitOverride(out _) : - this.IsMetadataVirtual(ignoreInterfaceImplementationChanges); + !this.IsStatic && this.IsMetadataVirtual(ignoreInterfaceImplementationChanges); } // TODO (tomat): sealed? @@ -447,6 +447,7 @@ internal override bool IsMetadataVirtual(bool ignoreInterfaceImplementationChang internal void EnsureMetadataVirtual() { + Debug.Assert(!this.IsStatic); this.flags.EnsureMetadataVirtual(); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs index 45091c65592df..4c2bce82cc32a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs @@ -60,7 +60,8 @@ protected SourceOrdinaryMethodSymbolBase( DeclarationModifiers declarationModifiers; (declarationModifiers, HasExplicitAccessModifier) = this.MakeModifiers(methodKind, isPartial, hasBody, location, diagnostics); - var isMetadataVirtualIgnoringModifiers = methodKind == MethodKind.ExplicitInterfaceImplementation; //explicit impls must be marked metadata virtual + //explicit impls must be marked metadata virtual unless static + var isMetadataVirtualIgnoringModifiers = methodKind == MethodKind.ExplicitInterfaceImplementation && (declarationModifiers & DeclarationModifiers.Static) == 0; this.MakeFlags(methodKind, declarationModifiers, returnsVoid, isExtensionMethod: isExtensionMethod, isNullableAnalysisEnabled: isNullableAnalysisEnabled, isMetadataVirtualIgnoringModifiers: isMetadataVirtualIgnoringModifiers); @@ -432,10 +433,18 @@ public override string Name DeclarationModifiers.AccessibilityMask; } } - else if (isInterface) + else { Debug.Assert(isExplicitInterfaceImplementation); - allowedModifiers |= DeclarationModifiers.Abstract; + + if (isInterface) + { + allowedModifiers |= DeclarationModifiers.Abstract; + } + else + { + allowedModifiers |= DeclarationModifiers.Static; + } } allowedModifiers |= DeclarationModifiers.Extern | DeclarationModifiers.Async; @@ -459,6 +468,16 @@ public override string Name hasExplicitAccessMod = true; } + if (isExplicitInterfaceImplementation && (mods & DeclarationModifiers.Static) != 0) + { + LanguageVersion availableVersion = ((CSharpParseOptions)location.SourceTree.Options).LanguageVersion; + LanguageVersion requiredVersion = MessageID.IDS_FeatureStaticAbstractMembersInInterfaces.RequiredVersion(); + if (availableVersion < requiredVersion) + { + ModifierUtils.ReportUnsupportedModifiersForLanguageVersion(mods, DeclarationModifiers.Static, location, diagnostics, availableVersion, requiredVersion); + } + } + this.CheckUnsafeModifier(mods, diagnostics); ModifierUtils.ReportDefaultInterfaceImplementationModifiers(hasBody, mods, diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs index b89065f06a1cf..f7ae1a79b2f20 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs @@ -316,6 +316,23 @@ void cacheResult(ImmutableArray result) } } +#nullable enable + internal sealed override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() + { + if (_unbound) + { + yield break; + } + + foreach ((MethodSymbol body, MethodSymbol implemented) in OriginalDefinition.SynthesizedInterfaceMethodImpls()) + { + var newBody = ExplicitInterfaceHelpers.SubstituteExplicitInterfaceImplementation(body, this.TypeSubstitution); + var newImplemented = ExplicitInterfaceHelpers.SubstituteExplicitInterfaceImplementation(implemented, this.TypeSubstitution); + yield return (newBody, newImplemented); + } + } +#nullable disable + internal override IEnumerable GetFieldsToEmit() { throw ExceptionUtilities.Unreachable; diff --git a/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs index b9fcdc08b673e..eb072757a2b2e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs @@ -531,7 +531,7 @@ internal static void GetTypeOrReturnType(this Symbol symbol, out RefKind refKind internal static bool IsImplementableInterfaceMember(this Symbol symbol) { - return !symbol.IsStatic && !symbol.IsSealed && (symbol.IsAbstract || symbol.IsVirtual) && (symbol.ContainingType?.IsInterface ?? false); + return !symbol.IsSealed && (symbol.IsAbstract || symbol.IsVirtual) && (symbol.ContainingType?.IsInterface ?? false); } internal static bool RequiresInstanceReceiver(this Symbol symbol) diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs index b86792454fe61..7d424e0ca1f77 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs @@ -221,5 +221,10 @@ internal override IEnumerable GetFieldsToEmit() internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable; internal sealed override NamedTypeSymbol NativeIntegerUnderlyingType => null; + + internal sealed override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() + { + return SpecializedCollections.EmptyEnumerable<(MethodSymbol Body, MethodSymbol Implemented)>(); + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs index fe1d12e16e612..e2c9e93e64fc7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs @@ -179,6 +179,11 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable; internal sealed override NamedTypeSymbol NativeIntegerUnderlyingType => null; + + internal sealed override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() + { + return SpecializedCollections.EmptyEnumerable<(MethodSymbol Body, MethodSymbol Implemented)>(); + } } /// diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedExplicitImplementationForwardingMethod.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedExplicitImplementationForwardingMethod.cs index 26508df4bfce7..2f0221466088f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedExplicitImplementationForwardingMethod.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedExplicitImplementationForwardingMethod.cs @@ -44,5 +44,7 @@ public override MethodKind MethodKind MethodKind.ExplicitInterfaceImplementation; } } + + public override bool IsStatic => _implementingMethod.IsStatic; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedImplementationMethod.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedImplementationMethod.cs index 9a4f8d20f6921..41804afd43139 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedImplementationMethod.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedImplementationMethod.cs @@ -225,20 +225,20 @@ internal sealed override bool RequiresSecurityObject internal sealed override bool IsMetadataVirtual(bool ignoreInterfaceImplementationChanges = false) { - return true; + return !IsStatic; } internal override bool IsMetadataFinal { get { - return true; + return !IsStatic; } } internal sealed override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) { - return true; + return !IsStatic; } public override DllImportData GetDllImportData() diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs index e63b07303b0d7..edeb5c3ae7d29 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs @@ -692,5 +692,10 @@ protected sealed override ITypeSymbol CreateITypeSymbol(CodeAnalysis.NullableAnn } internal override bool IsRecord => false; + + internal sealed override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() + { + return SpecializedCollections.EmptyEnumerable<(MethodSymbol Body, MethodSymbol Implemented)>(); + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.SymbolAndDiagnostics.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.SymbolAndDiagnostics.cs index 2c8d61d9b514e..19b3986bcb69a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.SymbolAndDiagnostics.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.SymbolAndDiagnostics.cs @@ -16,7 +16,7 @@ internal partial class TypeSymbol /// Represents the method by which this type implements a given interface type /// and/or the corresponding diagnostics. /// - protected class SymbolAndDiagnostics + internal class SymbolAndDiagnostics { public static readonly SymbolAndDiagnostics Empty = new SymbolAndDiagnostics(null, ImmutableBindingDiagnostic.Empty); diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs index 44ba88eb60fe8..83aa4fc8b29f7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs @@ -12,6 +12,7 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Symbols; using Roslyn.Utilities; @@ -80,13 +81,16 @@ public ConcurrentDictionary ImplementationForInter /// an error). /// internal MultiDictionary explicitInterfaceImplementationMap; - +#nullable enable + internal ImmutableDictionary? synthesizedMethodImplMap; +#nullable disable internal bool IsDefaultValue() { return allInterfaces.IsDefault && interfacesAndTheirBaseInterfaces == null && _implementationForInterfaceMemberMap == null && - explicitInterfaceImplementationMap == null; + explicitInterfaceImplementationMap == null && + synthesizedMethodImplMap == null; } } @@ -449,6 +453,11 @@ public Symbol FindImplementationForInterfaceMember(Symbol interfaceMember) if (this.IsInterfaceType()) { + if (interfaceMember.IsStatic) + { + return null; + } + var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; return FindMostSpecificImplementation(interfaceMember, (NamedTypeSymbol)this, ref discardedUseSiteInfo); } @@ -713,7 +722,7 @@ public ImmutableArray ToMinimalDisplayParts( /// - If the first request is done with false. A subsequent call /// is guaranteed to return the same result regardless of value. /// - protected SymbolAndDiagnostics FindImplementationForInterfaceMemberInNonInterfaceWithDiagnostics(Symbol interfaceMember, bool ignoreImplementationInInterfacesIfResultIsNotReady = false) + internal SymbolAndDiagnostics FindImplementationForInterfaceMemberInNonInterfaceWithDiagnostics(Symbol interfaceMember, bool ignoreImplementationInInterfacesIfResultIsNotReady = false) { Debug.Assert((object)interfaceMember != null); Debug.Assert(!this.IsInterfaceType()); @@ -821,7 +830,7 @@ private static Symbol ComputeImplementationForInterfaceMember(Symbol interfaceMe // // NOTE: The batch compiler is not affected by this discrepancy, since compilations don't call these // APIs on symbols from other compilations. - bool implementingTypeIsFromSomeCompilation = implementingType.Dangerous_IsFromSomeCompilation; + bool implementingTypeIsFromSomeCompilation = false; Symbol implicitImpl = null; Symbol closestMismatch = null; @@ -856,6 +865,18 @@ private static Symbol ComputeImplementationForInterfaceMember(Symbol interfaceMe return null; } + if (((object)currType != implementingType || !currType.IsDefinition) && interfaceMember is MethodSymbol interfaceMethod) + { + // Check for implementations that are going to be explicit once types are emitted + MethodSymbol bodyOfSynthesizedMethodImpl = currType.GetBodyOfSynthesizedInterfaceMethodImpl(interfaceMethod); + + if (bodyOfSynthesizedMethodImpl is object) + { + implementationInInterfacesMightChangeResult = false; + return bodyOfSynthesizedMethodImpl; + } + } + if (IsExplicitlyImplementedViaAccessors(interfaceMember, currType, out Symbol currTypeExplicitImpl)) { // We are looking for a property or event implementation and found an explicit implementation @@ -871,7 +892,11 @@ private static Symbol ComputeImplementationForInterfaceMember(Symbol interfaceMe { if (currType.InterfacesAndTheirBaseInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo).ContainsKey(interfaceType)) { - seenTypeDeclaringInterface = true; + if (!seenTypeDeclaringInterface) + { + implementingTypeIsFromSomeCompilation = currType.OriginalDefinition.ContainingModule is not PEModuleSymbol; + seenTypeDeclaringInterface = true; + } if ((object)currType == implementingType) { @@ -886,7 +911,8 @@ private static Symbol ComputeImplementationForInterfaceMember(Symbol interfaceMe // We want the implementation from the most derived type at or above the first one to // include the interface (or a subinterface) in its interface list - if (seenTypeDeclaringInterface) + if (seenTypeDeclaringInterface && + (!interfaceMember.IsStatic || implementingTypeIsFromSomeCompilation)) { //pass 2: check for implicit impls (name must match) Symbol currTypeImplicitImpl; @@ -914,7 +940,7 @@ private static Symbol ComputeImplementationForInterfaceMember(Symbol interfaceMe Debug.Assert(!canBeImplementedImplicitly || (object)implementingBaseOpt == null); - bool tryDefaultInterfaceImplementation = true; + bool tryDefaultInterfaceImplementation = !interfaceMember.IsStatic; // Dev10 has some extra restrictions and extra wiggle room when finding implicit // implementations for interface accessors. Perform some extra checks and possibly @@ -1003,6 +1029,7 @@ private static Symbol FindMostSpecificImplementationInInterfaces(Symbol interfac BindingDiagnosticBag diagnostics) { Debug.Assert(!implementingType.IsInterfaceType()); + Debug.Assert(!interfaceMember.IsStatic); // If we are dealing with a property or event and an implementation of at least one accessor is not from an interface, it // wouldn't be right to say that the event/property is implemented in an interface because its accessor isn't. @@ -1326,6 +1353,7 @@ static Symbol findMostSpecificImplementationInBases( internal static MultiDictionary.ValueSet FindImplementationInInterface(Symbol interfaceMember, NamedTypeSymbol interfaceType) { Debug.Assert(interfaceType.IsInterface); + Debug.Assert(!interfaceMember.IsStatic); NamedTypeSymbol containingType = interfaceMember.ContainingType; if (containingType.Equals(interfaceType, TypeCompareKind.CLRSignatureCompareOptions)) @@ -1427,7 +1455,7 @@ private static bool IsExplicitlyImplementedViaAccessors(Symbol interfaceMember, // If we haven't then there is no implementation. We need this check to match dev11 in some edge cases // (e.g. IndexerTests.AmbiguousExplicitIndexerImplementation). Such cases already fail // to roundtrip correctly, so it's not important to check for a particular compilation. - if ((object)implementingMember != null && implementingMember.Dangerous_IsFromSomeCompilation) + if ((object)implementingMember != null && implementingMember.OriginalDefinition.ContainingModule is not PEModuleSymbol) { implementingMember = null; } @@ -1545,6 +1573,7 @@ private static void CheckForImplementationOfCorrespondingPropertyOrEvent(MethodS interfaceMethod.Parameters, interfaceMethod.RefKind, interfaceMethod.IsInitOnly, + interfaceMethod.IsStatic, interfaceMethod.ReturnTypeWithAnnotations, interfaceMethod.RefCustomModifiers, interfaceMethod.ExplicitInterfaceImplementations); @@ -1653,6 +1682,13 @@ private static void ReportImplicitImplementationMatchDiagnostics(Symbol interfac } } } + + if (implicitImpl.IsStatic && !implementingType.ContainingAssembly.RuntimeSupportsStaticAbstractMembersInInterfaces) + { + diagnostics.Add(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, + GetInterfaceLocation(interfaceMember, implementingType), + implicitImpl, interfaceMember, implementingType); + } } internal static void CheckNullableReferenceTypeMismatchOnImplementingMember(TypeSymbol implementingType, Symbol implementingMember, Symbol interfaceMember, bool isExplicit, BindingDiagnosticBag diagnostics) @@ -1800,9 +1836,10 @@ private static void ReportImplicitImplementationMismatchDiagnostics(Symbol inter // Determine a better location for diagnostic squiggles. Squiggle the interface rather than the class. Location interfaceLocation = GetInterfaceLocation(interfaceMember, implementingType); - if (closestMismatch.IsStatic) + if (closestMismatch.IsStatic != interfaceMember.IsStatic) { - diagnostics.Add(ErrorCode.ERR_CloseUnimplementedInterfaceMemberStatic, interfaceLocation, implementingType, interfaceMember, closestMismatch); + diagnostics.Add(closestMismatch.IsStatic ? ErrorCode.ERR_CloseUnimplementedInterfaceMemberStatic : ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotStatic, + interfaceLocation, implementingType, interfaceMember, closestMismatch); } else if (closestMismatch.DeclaredAccessibility != Accessibility.Public) { @@ -2039,7 +2076,7 @@ private static void FindPotentialImplicitImplementationMemberDeclaredInType( /// private static bool IsInterfaceMemberImplementation(Symbol candidateMember, Symbol interfaceMember, bool implementingTypeIsFromSomeCompilation) { - if (candidateMember.DeclaredAccessibility != Accessibility.Public || candidateMember.IsStatic) + if (candidateMember.DeclaredAccessibility != Accessibility.Public || candidateMember.IsStatic != interfaceMember.IsStatic) { return false; } @@ -2096,6 +2133,63 @@ private MultiDictionary MakeExplicitInterfaceImplementationMap() return map; } +#nullable enable + /// + /// If implementation of an interface method will be accompanied with + /// a MethodImpl entry in metadata, information about which isn't already exposed through + /// API, this method returns the "Body" part + /// of the MethodImpl entry, i.e. the method that implements the . + /// Some of the MethodImpl entries could require synthetic forwarding methods. In such cases, + /// the result is the method that the language considers to implement the , + /// rather than the forwarding method. In other words, it is the method that the forwarding method forwards to. + /// + /// The interface method that is going to be implemented by using synthesized MethodImpl entry. + /// + protected MethodSymbol? GetBodyOfSynthesizedInterfaceMethodImpl(MethodSymbol interfaceMethod) + { + var info = this.GetInterfaceInfo(); + if (info == s_noInterfaces) + { + return null; + } + + if (info.synthesizedMethodImplMap == null) + { + Interlocked.CompareExchange(ref info.synthesizedMethodImplMap, makeSynthesizedMethodImplMap(), null); + } + + if (info.synthesizedMethodImplMap.TryGetValue(interfaceMethod, out MethodSymbol? result)) + { + return result; + } + + return null; + + ImmutableDictionary makeSynthesizedMethodImplMap() + { + var map = ImmutableDictionary.CreateBuilder(ExplicitInterfaceImplementationTargetMemberEqualityComparer.Instance); + foreach ((MethodSymbol body, MethodSymbol implemented) in this.SynthesizedInterfaceMethodImpls()) + { + map.Add(implemented, body); + } + + return map.ToImmutable(); + } + } + + /// + /// Returns information about interface method implementations that will be accompanied with + /// MethodImpl entries in metadata, information about which isn't already exposed through + /// API. The "Body" is the method that + /// implements the interface method "Implemented". + /// Some of the MethodImpl entries could require synthetic forwarding methods. In such cases, + /// the "Body" is the method that the language considers to implement the interface method, + /// the "Implemented", rather than the forwarding method. In other words, it is the method that + /// the forwarding method forwards to. + /// + internal abstract IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls(); +#nullable disable + protected class ExplicitInterfaceImplementationTargetMemberEqualityComparer : IEqualityComparer { public static readonly ExplicitInterfaceImplementationTargetMemberEqualityComparer Instance = new ExplicitInterfaceImplementationTargetMemberEqualityComparer(); diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 85459ab5a6865..e4a617f0239b7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -232,6 +232,11 @@ Členy s názvem Clone se v záznamech nepovolují. + + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + + '{0}' does not implement interface member '{1}'. '{2}' cannot implement '{1}'. {0} neimplementuje člen rozhraní {1}. {2} nemůže implementovat {1}. @@ -872,6 +877,11 @@ Target runtime doesn't support static abstract members in interfaces. + + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. Cílový modul runtime nepodporuje rozšiřitelné konvence volání ani konvence volání výchozí pro prostředí modulu runtime. @@ -5826,8 +5836,8 @@ Pokud se taková třída používá jako základní třída a pokud odvozující - '{0}' does not implement interface member '{1}'. '{2}' cannot implement an interface member because it is static. - {0} neimplementuje člen rozhraní {1}. {2} nemůže implementovat člen rozhraní, protože je statické. + '{0}' does not implement instance interface member '{1}'. '{2}' cannot implement the interface member because it is static. + {0} neimplementuje člen rozhraní {1}. {2} nemůže implementovat člen rozhraní, protože je statické. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 306d6b1685085..e9efa301d8667 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -232,6 +232,11 @@ Member mit dem Namen "Clone" sind in Datensätzen nicht zulässig. + + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + + '{0}' does not implement interface member '{1}'. '{2}' cannot implement '{1}'. Der Schnittstellenmember "{1}" wird von "{0}" nicht implementiert. "{1}" kann von "{2}" nicht implementiert werden. @@ -872,6 +877,11 @@ Target runtime doesn't support static abstract members in interfaces. + + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. Die Zielruntime unterstützt keine erweiterbaren Aufrufkonventionen oder Standardaufrufkonventionen der Runtime-Umgebung. @@ -5826,8 +5836,8 @@ Wenn solch eine Klasse als Basisklasse verwendet wird und die ableitende Klasse - '{0}' does not implement interface member '{1}'. '{2}' cannot implement an interface member because it is static. - "{0}" implementiert den Schnittstellenmember "{1}" nicht. "{2}" ist statisch und kann daher keinen Schnittstellenmember implementieren. + '{0}' does not implement instance interface member '{1}'. '{2}' cannot implement the interface member because it is static. + "{0}" implementiert den Schnittstellenmember "{1}" nicht. "{2}" ist statisch und kann daher keinen Schnittstellenmember implementieren. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 78f4e984374c1..2fa1c89a4544e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -232,6 +232,11 @@ No se permiten los miembros denominados "Clone" en los registros. + + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + + '{0}' does not implement interface member '{1}'. '{2}' cannot implement '{1}'. "{0}" no implementa el miembro de interfaz "{1}". "{2}" no puede implementar "{1}". @@ -872,6 +877,11 @@ Target runtime doesn't support static abstract members in interfaces. + + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. El entorno de ejecución de destino no admite convenciones de llamada predeterminadas de entorno en tiempo de ejecución o extensible. @@ -5826,8 +5836,8 @@ Si se utiliza una clase de este tipo como clase base y si la clase derivada defi - '{0}' does not implement interface member '{1}'. '{2}' cannot implement an interface member because it is static. - '{0}' no implementa el miembro de interfaz '{1}'. '{2}' no puede implementar un miembro de interfaz porque es estático. + '{0}' does not implement instance interface member '{1}'. '{2}' cannot implement the interface member because it is static. + '{0}' no implementa el miembro de interfaz '{1}'. '{2}' no puede implementar un miembro de interfaz porque es estático. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 207fb680a8490..37917d4b98f56 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -232,6 +232,11 @@ Les membres nommés 'Clone' ne sont pas autorisés dans les enregistrements. + + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + + '{0}' does not implement interface member '{1}'. '{2}' cannot implement '{1}'. '{0}' n'implémente pas le membre d'interface '{1}'. '{2}' ne peut pas implémenter '{1}'. @@ -872,6 +877,11 @@ Target runtime doesn't support static abstract members in interfaces. + + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. Le runtime cible ne prend pas en charge les conventions d'appel par défaut des environnements extensibles ou d'exécution. @@ -5826,8 +5836,8 @@ Si une telle classe est utilisée en tant que classe de base et si la classe dé - '{0}' does not implement interface member '{1}'. '{2}' cannot implement an interface member because it is static. - '{0}' n'implémente pas le membre d'interface '{1}'. '{2}' ne peut pas implémenter un membre d'interface, car il est static. + '{0}' does not implement instance interface member '{1}'. '{2}' cannot implement the interface member because it is static. + '{0}' n'implémente pas le membre d'interface '{1}'. '{2}' ne peut pas implémenter un membre d'interface, car il est static. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 735cea16367f4..2f1b62b0561f1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -232,6 +232,11 @@ Nei record non sono consentiti membri denominati 'Clone'. + + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + + '{0}' does not implement interface member '{1}'. '{2}' cannot implement '{1}'. '{0}' non implementa il membro di interfaccia '{1}'. '{2}' non può implementare '{1}'. @@ -872,6 +877,11 @@ Target runtime doesn't support static abstract members in interfaces. + + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. Il runtime di destinazione non supporta convenzioni di chiamata predefinite estendibili o dell'ambiente di runtime. @@ -5826,8 +5836,8 @@ Se si usa tale classe come classe base e se la classe di derivazione definisce u - '{0}' does not implement interface member '{1}'. '{2}' cannot implement an interface member because it is static. - '{0}' non implementa il membro di interfaccia '{1}'. '{2}' non può implementare un membro di interfaccia perché è di tipo statico. + '{0}' does not implement instance interface member '{1}'. '{2}' cannot implement the interface member because it is static. + '{0}' non implementa il membro di interfaccia '{1}'. '{2}' non può implementare un membro di interfaccia perché è di tipo statico. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index c5bbd062f2067..2aa39b069e9c9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -232,6 +232,11 @@ 'Clone' という名前のメンバーはレコードでは許可されていません。 + + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + + '{0}' does not implement interface member '{1}'. '{2}' cannot implement '{1}'. '{0}' は、インターフェイス メンバー '{1}' を実装していません。'{2}' は '{1}' を実装できません。 @@ -872,6 +877,11 @@ Target runtime doesn't support static abstract members in interfaces. + + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. ターゲット ランタイムは、拡張可能またはランタイム環境の既定の呼び出し規則をサポートしていません。 @@ -5826,8 +5836,8 @@ If such a class is used as a base class and if the deriving class defines a dest - '{0}' does not implement interface member '{1}'. '{2}' cannot implement an interface member because it is static. - '{0}' は、インターフェイス メンバー '{1}' を実装していません。'{2}' は static のため、インターフェイス メンバーを実装できません。 + '{0}' does not implement instance interface member '{1}'. '{2}' cannot implement the interface member because it is static. + '{0}' は、インターフェイス メンバー '{1}' を実装していません。'{2}' は static のため、インターフェイス メンバーを実装できません。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index e898b1a74dc68..0fc3026a49133 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -232,6 +232,11 @@ 'Clone'이라는 멤버는 레코드에서 허용되지 않습니다. + + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + + '{0}' does not implement interface member '{1}'. '{2}' cannot implement '{1}'. '{0}'은(는) 인터페이스 멤버 '{1}'을(를) 구현하지 않습니다. '{2}'은(는) '{1}'을(를) 구현할 수 없습니다. @@ -872,6 +877,11 @@ Target runtime doesn't support static abstract members in interfaces. + + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. 대상 런타임에서 확장 가능 또는 런타임 환경 기본 호출 규칙을 지원하지 않습니다. @@ -5825,8 +5835,8 @@ If such a class is used as a base class and if the deriving class defines a dest - '{0}' does not implement interface member '{1}'. '{2}' cannot implement an interface member because it is static. - '{0}'은(는) '{1}' 인터페이스 멤버를 구현하지 않습니다. '{2}'은(는) static이므로 인터페이스 멤버를 구현할 수 없습니다. + '{0}' does not implement instance interface member '{1}'. '{2}' cannot implement the interface member because it is static. + '{0}'은(는) '{1}' 인터페이스 멤버를 구현하지 않습니다. '{2}'은(는) static이므로 인터페이스 멤버를 구현할 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index a1f4297bc7075..eb04edb681e8d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -232,6 +232,11 @@ Składowe o nazwie „Clone” są niedozwolone w rekordach. + + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + + '{0}' does not implement interface member '{1}'. '{2}' cannot implement '{1}'. Element „{0}” nie implementuje składowej interfejsu „{1}”. Element „{2}” nie może implementować elementu „{1}”. @@ -872,6 +877,11 @@ Target runtime doesn't support static abstract members in interfaces. + + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. Docelowe środowisko uruchomieniowe nie obsługuje rozszerzalnych ani domyślnych dla środowiska uruchomieniowego konwencji wywoływania. @@ -5826,8 +5836,8 @@ Jeśli taka klasa zostanie użyta jako klasa bazowa i klasa pochodna definiuje d - '{0}' does not implement interface member '{1}'. '{2}' cannot implement an interface member because it is static. - 'Element „{0}” nie implementuje składowej interfejsu „{1}”. Element „{2}” nie może implementować składowej interfejsu, ponieważ jest statyczna. + '{0}' does not implement instance interface member '{1}'. '{2}' cannot implement the interface member because it is static. + 'Element „{0}” nie implementuje składowej interfejsu „{1}”. Element „{2}” nie może implementować składowej interfejsu, ponieważ jest statyczna. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 6f43a853cc189..e9a8afe2f0c1e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -232,6 +232,11 @@ Os membros chamados 'Clone' não são permitidos nos registros. + + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + + '{0}' does not implement interface member '{1}'. '{2}' cannot implement '{1}'. '{0}' não implementa o membro de interface '{1}'. '{2}' não pode implementar '{1}'. @@ -872,6 +877,11 @@ Target runtime doesn't support static abstract members in interfaces. + + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. O runtime de destino não dá suporte a convenções de chamada padrão extensíveis ou de ambiente de runtime. @@ -5826,8 +5836,8 @@ Se tal classe for usada como uma classe base e se a classe derivada definir um d - '{0}' does not implement interface member '{1}'. '{2}' cannot implement an interface member because it is static. - "{0}" não implementa membro de interface "{1}". "{2}" não pode implementar um membro de interface, pois é estático. + '{0}' does not implement instance interface member '{1}'. '{2}' cannot implement the interface member because it is static. + "{0}" não implementa membro de interface "{1}". "{2}" não pode implementar um membro de interface, pois é estático. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index b2b6d0dc63761..e173fb6641d14 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -232,6 +232,11 @@ Элементы с именем "Clone" не могут использоваться в записях. + + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + + '{0}' does not implement interface member '{1}'. '{2}' cannot implement '{1}'. "{0}" не реализует элемент интерфейса "{1}". "{2}" не может реализовывать "{1}". @@ -872,6 +877,11 @@ Target runtime doesn't support static abstract members in interfaces. + + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. Целевая среда выполнения не поддерживает расширяемые или принадлежащие среде выполнения соглашения о вызовах по умолчанию. @@ -5826,8 +5836,8 @@ If such a class is used as a base class and if the deriving class defines a dest - '{0}' does not implement interface member '{1}'. '{2}' cannot implement an interface member because it is static. - "{0}" не реализует член интерфейса "{1}". '{2}" не может реализовать член интерфейса, потому что он является статическим. + '{0}' does not implement instance interface member '{1}'. '{2}' cannot implement the interface member because it is static. + "{0}" не реализует член интерфейса "{1}". '{2}" не может реализовать член интерфейса, потому что он является статическим. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index a72d919f99e56..ee2210a026767 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -232,6 +232,11 @@ 'Clone' adlı üyelere kayıtlarda izin verilmez. + + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + + '{0}' does not implement interface member '{1}'. '{2}' cannot implement '{1}'. '{0}, '{1}' arabirim üyesini uygulamıyor. '{2}', '{1}' üyesini uygulayamaz. @@ -872,6 +877,11 @@ Target runtime doesn't support static abstract members in interfaces. + + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. Hedef çalışma zamanı, genişletilebilir veya çalışma zamanı ortamı varsayılanı çağırma kurallarını desteklemiyor. @@ -5826,8 +5836,8 @@ Bu sınıf temel sınıf olarak kullanılırsa ve türetilen sınıf bir yıkıc - '{0}' does not implement interface member '{1}'. '{2}' cannot implement an interface member because it is static. - '{0}, '{1}' arabirim üyesini uygulamıyor. '{2}' statik olduğundan bir arabirim üyesi uygulayamaz. + '{0}' does not implement instance interface member '{1}'. '{2}' cannot implement the interface member because it is static. + '{0}, '{1}' arabirim üyesini uygulamıyor. '{2}' statik olduğundan bir arabirim üyesi uygulayamaz. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 796e07510c873..c03024a2a223d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -232,6 +232,11 @@ 记录中不允许使用名为 "Clone" 的成员。 + + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + + '{0}' does not implement interface member '{1}'. '{2}' cannot implement '{1}'. “{0}”不实现接口成员“{1}”。“{2}”无法实现“{1}”。 @@ -872,6 +877,11 @@ Target runtime doesn't support static abstract members in interfaces. + + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. 目标运行时不支持可扩展或运行时环境默认调用约定。 @@ -5831,8 +5841,8 @@ If such a class is used as a base class and if the deriving class defines a dest - '{0}' does not implement interface member '{1}'. '{2}' cannot implement an interface member because it is static. - “{0}”不实现接口成员“{1}”。“{2}”无法实现接口成员,因为它是静态的。 + '{0}' does not implement instance interface member '{1}'. '{2}' cannot implement the interface member because it is static. + “{0}”不实现接口成员“{1}”。“{2}”无法实现接口成员,因为它是静态的。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 1fa7ed8f0559d..344e3bea2ed80 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -232,6 +232,11 @@ 記錄中不允許名為 'Clone' 的成員。 + + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + '{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static. + + '{0}' does not implement interface member '{1}'. '{2}' cannot implement '{1}'. '{0}' 未實作介面成員 '{1}'。'{2}' 無法實作 '{1}'。 @@ -872,6 +877,11 @@ Target runtime doesn't support static abstract members in interfaces. + + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + + The target runtime doesn't support extensible or runtime-environment default calling conventions. 目標執行階段不支援可延伸或執行階段環境的預設呼叫慣例。 @@ -5826,8 +5836,8 @@ If such a class is used as a base class and if the deriving class defines a dest - '{0}' does not implement interface member '{1}'. '{2}' cannot implement an interface member because it is static. - '{0}' 未實作介面成員 '{1}'。'{2}' 無法實作介面成員,因為其為靜態。 + '{0}' does not implement instance interface member '{1}'. '{2}' cannot implement the interface member because it is static. + '{0}' 未實作介面成員 '{1}'。'{2}' 無法實作介面成員,因為其為靜態。 diff --git a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs index fe1000a62b48a..a06040ba8b4c2 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs @@ -2419,7 +2419,7 @@ public class Class1 : CppCli.CppBase2, CppCli.CppInterface1 var class1TypeDef = (Cci.ITypeDefinition)class1.GetCciAdapter(); - var symbolSynthesized = class1.GetSynthesizedExplicitImplementations(CancellationToken.None); + var symbolSynthesized = class1.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods; var context = new EmitContext(module, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true); var cciExplicit = class1TypeDef.GetExplicitImplementationOverrides(context); var cciMethods = class1TypeDef.GetMethods(context).Where(m => ((MethodSymbol)m.GetInternalSymbol()).MethodKind != MethodKind.Constructor); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs index 0c28203a20a49..42fb8ad138020 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs @@ -99,15 +99,21 @@ private protected void IGoo.Method14() { } // (34,23): error CS0106: The modifier 'private' is not valid for this item // private void IGoo.Method10() { } Diagnostic(ErrorCode.ERR_BadMemberFlag, "Method10").WithArguments("private").WithLocation(34, 23), - // (37,22): error CS0106: The modifier 'static' is not valid for this item + // (37,22): error CS8703: The modifier 'static' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. // static void IGoo.Method12() { } - Diagnostic(ErrorCode.ERR_BadMemberFlag, "Method12").WithArguments("static").WithLocation(37, 22), + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "Method12").WithArguments("static", "9.0", "preview").WithLocation(37, 22), // (40,33): error CS0106: The modifier 'private protected' is not valid for this item // private protected void IGoo.Method14() { } Diagnostic(ErrorCode.ERR_BadMemberFlag, "Method14").WithArguments("private protected").WithLocation(40, 33), + // (37,22): error CS0539: 'AbstractGoo.Method12()' in explicit interface declaration is not found among members of the interface that can be implemented + // static void IGoo.Method12() { } + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "Method12").WithArguments("AbstractGoo.Method12()").WithLocation(37, 22), // (38,23): error CS0754: A partial method may not explicitly implement an interface method // partial void IGoo.Method13(); Diagnostic(ErrorCode.ERR_PartialMethodNotExplicit, "Method13").WithLocation(38, 23), + // (20,38): error CS0535: 'AbstractGoo' does not implement interface member 'IGoo.Method12()' + // abstract partial class AbstractGoo : IGoo + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "IGoo").WithArguments("AbstractGoo", "IGoo.Method12()").WithLocation(20, 38), // (36,22): warning CS0626: Method, operator, or accessor 'AbstractGoo.IGoo.Method11()' is marked external and has no attributes on it. Consider adding a DllImport attribute to specify the external implementation. // extern void IGoo.Method11(); //not an error (in dev10 or roslyn) Diagnostic(ErrorCode.WRN_ExternMethodNoImplementation, "Method11").WithArguments("AbstractGoo.IGoo.Method11()").WithLocation(36, 22) @@ -3688,7 +3694,7 @@ protected internal void Method6() { } // (13,22): error CS0737: 'Base' does not implement interface member 'Interface.Method6()'. 'Base.Method6()' cannot implement an interface member because it is not public. // partial class Base : Interface Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotPublic, "Interface").WithArguments("Base", "Interface.Method6()", "Base.Method6()").WithLocation(13, 22), - // (13,22): error CS0736: 'Base' does not implement interface member 'Interface.Method1()'. 'Base.Method1()' cannot implement an interface member because it is static. + // (13,22): error CS0736: 'Base' does not implement instance interface member 'Interface.Method1()'. 'Base.Method1()' cannot implement the interface member because it is static. // partial class Base : Interface Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberStatic, "Interface").WithArguments("Base", "Interface.Method1()", "Base.Method1()").WithLocation(13, 22)); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs index 07ccbfca40513..9f4e7bd9c0289 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs @@ -18601,7 +18601,7 @@ record B : A; "; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (2,8): error CS0736: 'A' does not implement interface member 'IEquatable.Equals(A)'. 'A.Equals(A)' cannot implement an interface member because it is static. + // (2,8): error CS0736: 'A' does not implement instance interface member 'IEquatable.Equals(A)'. 'A.Equals(A)' cannot implement the interface member because it is static. // record A Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberStatic, "A").WithArguments("A", "System.IEquatable.Equals(A)", "A.Equals(A)").WithLocation(2, 8), // (4,24): error CS8872: 'A.Equals(A)' must allow overriding because the containing record is not sealed. @@ -18628,7 +18628,7 @@ sealed record A "; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (2,15): error CS0736: 'A' does not implement interface member 'IEquatable.Equals(A)'. 'A.Equals(A)' cannot implement an interface member because it is static. + // (2,15): error CS0736: 'A' does not implement instance interface member 'IEquatable.Equals(A)'. 'A.Equals(A)' cannot implement the interface member because it is static. // sealed record A Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberStatic, "A").WithArguments("A", "System.IEquatable.Equals(A)", "A.Equals(A)").WithLocation(2, 15), // (4,24): error CS8877: Record member 'A.Equals(A)' may not be static. @@ -20498,7 +20498,7 @@ public static bool Equals(A other) "; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (2,8): error CS0736: 'A' does not implement interface member 'IEquatable.Equals(A)'. 'A.Equals(A)' cannot implement an interface member because it is static. + // (2,8): error CS0736: 'A' does not implement instance interface member 'IEquatable.Equals(A)'. 'A.Equals(A)' cannot implement the interface member because it is static. // record A Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberStatic, "A").WithArguments("A", "System.IEquatable.Equals(A)", "A.Equals(A)").WithLocation(2, 8), // (4,24): error CS8872: 'A.Equals(A)' must allow overriding because the containing record is not sealed. diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/AccessorOverriddenOrHiddenMembersTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/AccessorOverriddenOrHiddenMembersTests.cs index 0d1ec556a82a9..4a00528652828 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/AccessorOverriddenOrHiddenMembersTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/AccessorOverriddenOrHiddenMembersTests.cs @@ -352,7 +352,7 @@ public class C : I Assert.NotEqual(ilGetter.Name, csharpGetter.Name); //name not copied - var bridge = @class.GetSynthesizedExplicitImplementations(CancellationToken.None).Single(); + var bridge = @class.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Single(); Assert.Same(csharpGetter, bridge.ImplementingMethod); Assert.Same(ilGetter, bridge.ExplicitInterfaceImplementations.Single()); @@ -384,7 +384,7 @@ public class C : I var csharpGetter = @class.GetProperty("I.P").GetMethod; Assert.Equal("I.getter", csharpGetter.Name); - Assert.Equal(0, @class.GetSynthesizedExplicitImplementations(CancellationToken.None).Length); //not needed + Assert.Equal(0, @class.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Length); //not needed } [Fact] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs index 52ce01b7d9c1d..e625cfb28cb32 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs @@ -7026,7 +7026,13 @@ class Test2 : I1 Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M2").WithArguments("Test1.M2()").WithLocation(23, 13), // (24,13): error CS0539: 'Test1.M3()' in explicit interface declaration is not found among members of the interface that can be implemented // void I1.M3() {} - Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M3").WithArguments("Test1.M3()").WithLocation(24, 13) + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M3").WithArguments("Test1.M3()").WithLocation(24, 13), + // (19,15): error CS0535: 'Test1' does not implement interface member 'I1.M1()' + // class Test1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("Test1", "I1.M1()").WithLocation(19, 15), + // (27,15): error CS0535: 'Test2' does not implement interface member 'I1.M1()' + // class Test2 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("Test2", "I1.M1()").WithLocation(27, 15) ); var test1 = compilation1.GetTypeByMetadataName("Test1"); @@ -8610,6 +8616,9 @@ void I1.M5() {} // (11,15): error CS0535: 'Test1' does not implement interface member 'I1.M2()' // class Test1 : I1 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("Test1", "I1.M2()").WithLocation(11, 15), + // (11,15): error CS0535: 'Test1' does not implement interface member 'I1.M3()' + // class Test1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("Test1", "I1.M3()").WithLocation(11, 15), // (11,15): error CS0535: 'Test1' does not implement interface member 'I1.M1()' // class Test1 : I1 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("Test1", "I1.M1()").WithLocation(11, 15), @@ -8624,7 +8633,10 @@ void I1.M5() {} Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M4").WithArguments("Test2.M4()").WithLocation(20, 13), // (21,13): error CS0539: 'Test2.M5()' in explicit interface declaration is not found among members of the interface that can be implemented // void I1.M5() {} - Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M5").WithArguments("Test2.M5()").WithLocation(21, 13) + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M5").WithArguments("Test2.M5()").WithLocation(21, 13), + // (15,15): error CS0535: 'Test2' does not implement interface member 'I1.M3()' + // class Test2 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("Test2", "I1.M3()").WithLocation(15, 15) ); var test1 = compilation1.GetTypeByMetadataName("Test1"); @@ -12067,6 +12079,9 @@ class Test2 : I1 // (8,23): error CS8703: The modifier 'sealed' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. // sealed static int P3 => 0; Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "P3").WithArguments("sealed", "9.0", "preview").WithLocation(8, 23), + // (13,15): error CS0535: 'Test1' does not implement interface member 'I1.P1' + // class Test1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("Test1", "I1.P1").WithLocation(13, 15), // (15,12): error CS0539: 'Test1.P1' in explicit interface declaration is not found among members of the interface that can be implemented // int I1.P1 => 0; Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "P1").WithArguments("Test1.P1").WithLocation(15, 12), @@ -12078,7 +12093,10 @@ class Test2 : I1 Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "P3").WithArguments("Test1.P3").WithLocation(17, 12), // (18,12): error CS0539: 'Test1.P4' in explicit interface declaration is not found among members of the interface that can be implemented // int I1.P4 {set {}} - Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "P4").WithArguments("Test1.P4").WithLocation(18, 12) + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "P4").WithArguments("Test1.P4").WithLocation(18, 12), + // (21,15): error CS0535: 'Test2' does not implement interface member 'I1.P1' + // class Test2 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("Test2", "I1.P1").WithLocation(21, 15) ); var test1 = compilation1.GetTypeByMetadataName("Test1"); @@ -14744,7 +14762,13 @@ class Test2 : I1, I2, I3, I4, I5 Diagnostic(ErrorCode.WRN_ExternMethodNoImplementation, "get").WithArguments("I3.P3.get").WithLocation(12, 27), // (12,32): warning CS0626: Method, operator, or accessor 'I3.P3.set' is marked external and has no attributes on it. Consider adding a DllImport attribute to specify the external implementation. // static extern int P3 {get; set;} - Diagnostic(ErrorCode.WRN_ExternMethodNoImplementation, "set").WithArguments("I3.P3.set").WithLocation(12, 32) + Diagnostic(ErrorCode.WRN_ExternMethodNoImplementation, "set").WithArguments("I3.P3.set").WithLocation(12, 32), + // (23,27): error CS0535: 'Test1' does not implement interface member 'I4.P4' + // class Test1 : I1, I2, I3, I4, I5 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I4").WithArguments("Test1", "I4.P4").WithLocation(23, 27), + // (27,27): error CS0535: 'Test2' does not implement interface member 'I4.P4' + // class Test2 : I1, I2, I3, I4, I5 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I4").WithArguments("Test2", "I4.P4").WithLocation(27, 27) ); } @@ -24909,7 +24933,13 @@ class Test2 : I1 Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "P2").WithArguments("Test1.P2").WithLocation(14, 28), // (15,28): error CS0539: 'Test1.P3' in explicit interface declaration is not found among members of the interface that can be implemented // event System.Action I1.P3 {add {} remove{}} - Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "P3").WithArguments("Test1.P3").WithLocation(15, 28) + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "P3").WithArguments("Test1.P3").WithLocation(15, 28), + // (18,15): error CS0535: 'Test2' does not implement interface member 'I1.P1' + // class Test2 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("Test2", "I1.P1").WithLocation(18, 15), + // (11,15): error CS0535: 'Test1' does not implement interface member 'I1.P1' + // class Test1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("Test1", "I1.P1").WithLocation(11, 15) ); var test1 = compilation1.GetTypeByMetadataName("Test1"); @@ -27394,6 +27424,12 @@ class Test2 : I1, I2, I3, I4, I5 // (12,39): warning CS0626: Method, operator, or accessor 'I3.P3.remove' is marked external and has no attributes on it. Consider adding a DllImport attribute to specify the external implementation. // static extern event System.Action P3; Diagnostic(ErrorCode.WRN_ExternMethodNoImplementation, "P3").WithArguments("I3.P3.remove").WithLocation(12, 39), + // (23,27): error CS0535: 'Test1' does not implement interface member 'I4.P4' + // class Test1 : I1, I2, I3, I4, I5 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I4").WithArguments("Test1", "I4.P4").WithLocation(23, 27), + // (27,27): error CS0535: 'Test2' does not implement interface member 'I4.P4' + // class Test2 : I1, I2, I3, I4, I5 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I4").WithArguments("Test2", "I4.P4").WithLocation(27, 27), // (8,42): warning CS0067: The event 'I2.P2' is never used // abstract private event System.Action P2 = null; Diagnostic(ErrorCode.WRN_UnreferencedEvent, "P2").WithArguments("I2.P2").WithLocation(8, 42) diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/IndexerTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/IndexerTests.cs index 421d76ae26e44..cf9f689556b1f 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/IndexerTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/IndexerTests.cs @@ -145,7 +145,7 @@ public object this[string index] var sourceType = globalNamespace.GetMember("B"); CheckIndexer(sourceType.Indexers.Single(), true, true, SpecialType.System_Object, SpecialType.System_String); - var bridgeMethods = sourceType.GetSynthesizedExplicitImplementations(CancellationToken.None); + var bridgeMethods = sourceType.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods; Assert.Equal(2, bridgeMethods.Length); Assert.True(bridgeMethods.Select(GetPairForSynthesizedExplicitImplementation).SetEquals(new[] { @@ -156,7 +156,7 @@ public object this[string index] sourceType = globalNamespace.GetMember("C"); CheckIndexer(sourceType.Indexers.Single(), true, true, SpecialType.System_Object, SpecialType.System_String); - bridgeMethods = sourceType.GetSynthesizedExplicitImplementations(CancellationToken.None); + bridgeMethods = sourceType.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods; Assert.Equal(3, bridgeMethods.Length); Assert.True(bridgeMethods.Select(GetPairForSynthesizedExplicitImplementation).SetEquals(new[] { @@ -335,7 +335,7 @@ class C : I1, I2 Assert.Equal(classIndexer, @class.FindImplementationForInterfaceMember(interface1Indexer)); Assert.Equal(classIndexer, @class.FindImplementationForInterfaceMember(interface2Indexer)); - var synthesizedExplicitImplementations = @class.GetSynthesizedExplicitImplementations(default(CancellationToken)); + var synthesizedExplicitImplementations = @class.GetSynthesizedExplicitImplementations(default(CancellationToken)).ForwardingMethods; Assert.Equal(2, synthesizedExplicitImplementations.Length); Assert.Equal(classIndexer.GetMethod, synthesizedExplicitImplementations[0].ImplementingMethod); @@ -417,7 +417,7 @@ class C : I1, I2 Assert.Equal(classIndexer, @class.FindImplementationForInterfaceMember(interface1Indexer)); Assert.Equal(classIndexer, @class.FindImplementationForInterfaceMember(interface2Indexer)); - var synthesizedExplicitImplementations = @class.GetSynthesizedExplicitImplementations(default(CancellationToken)); + var synthesizedExplicitImplementations = @class.GetSynthesizedExplicitImplementations(default(CancellationToken)).ForwardingMethods; Assert.Equal(2, synthesizedExplicitImplementations.Length); Assert.Equal(classIndexer.GetMethod, synthesizedExplicitImplementations[0].ImplementingMethod); @@ -484,7 +484,7 @@ class C : I1 Assert.Equal(classIndexer, @class.FindImplementationForInterfaceMember(interfaceIndexers[0])); Assert.Equal(classIndexer, @class.FindImplementationForInterfaceMember(interfaceIndexers[1])); - var synthesizedExplicitImplementation = @class.GetSynthesizedExplicitImplementations(default(CancellationToken)).Single(); + var synthesizedExplicitImplementation = @class.GetSynthesizedExplicitImplementations(default(CancellationToken)).ForwardingMethods.Single(); Assert.Equal(classIndexer.GetMethod, synthesizedExplicitImplementation.ImplementingMethod); @@ -524,14 +524,14 @@ .property instance int32 B(int32) } // end of class I1 "; - var csharp = @" + var csharp1 = @" class C : I1 { int I1.this[int x] { get { return 0; } } } "; - var compilation = CreateCompilationWithILAndMscorlib40(csharp, il).VerifyDiagnostics( + var compilation = CreateCompilationWithILAndMscorlib40(csharp1, il).VerifyDiagnostics( // (4,12): warning CS0473: Explicit interface implementation 'C.I1.this[int]' matches more than one interface member. Which interface member is actually chosen is implementation-dependent. Consider using a non-explicit implementation instead. Diagnostic(ErrorCode.WRN_ExplicitImplCollision, "this").WithArguments("C.I1.this[int]"), // (2,7): error CS0535: 'C' does not implement interface member 'I1.this[int]' @@ -551,6 +551,15 @@ class C : I1 var indexer1Impl = @class.FindImplementationForInterfaceMember(interfaceIndexers[1]); Assert.True(indexer0Impl == classIndexer ^ indexer1Impl == classIndexer); Assert.True(indexer0Impl == null ^ indexer1Impl == null); + + var csharp2 = @" +class C : I1 +{ + public int this[int x] { get { return 0; } } +} +"; + + compilation = CreateCompilationWithILAndMscorlib40(csharp2, il).VerifyDiagnostics(); } [ClrOnlyFact(ClrOnlyReason.Ilasm)] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/InterfaceImplementationTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/InterfaceImplementationTests.cs index a90947f957bb3..d1469451ea66f 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/InterfaceImplementationTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/InterfaceImplementationTests.cs @@ -939,7 +939,7 @@ public class Derived : Base, Interface Assert.True(((Cci.IMethodDefinition)baseClassPropertyGetter.GetCciAdapter()).IsVirtual); Assert.True(((Cci.IMethodDefinition)baseClassPropertySetter.GetCciAdapter()).IsVirtual); - Assert.False(derivedClass.GetSynthesizedExplicitImplementations(CancellationToken.None).Any()); + Assert.False(derivedClass.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Any()); } [Fact] @@ -1010,7 +1010,7 @@ public class Derived : Base, Interface // GetSynthesizedExplicitImplementations doesn't guarantee order, so sort to make the asserts easier to write. - var synthesizedExplicitImpls = (from m in derivedClass.GetSynthesizedExplicitImplementations(CancellationToken.None) orderby m.MethodKind select m).ToArray(); + var synthesizedExplicitImpls = (from m in derivedClass.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods orderby m.MethodKind select m).ToArray(); Assert.Equal(3, synthesizedExplicitImpls.Length); Assert.True(synthesizedExplicitImpls.All(s => ReferenceEquals(derivedClass, s.ContainingType))); @@ -1079,7 +1079,7 @@ class Class : CustomModifierOverridingD, Interface // GetSynthesizedExplicitImplementations doesn't guarantee order, so sort to make the asserts easier to write. - var synthesizedExplicitImpls = (from m in @class.GetSynthesizedExplicitImplementations(CancellationToken.None) orderby m.Name select m).ToArray(); + var synthesizedExplicitImpls = (from m in @class.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods orderby m.Name select m).ToArray(); Assert.Equal(2, synthesizedExplicitImpls.Length); var synthesizedExplicitMethod1Impl = synthesizedExplicitImpls[0]; @@ -1652,7 +1652,7 @@ class C : B, I { } Assert.Equal(classBMethod, classC.FindImplementationForInterfaceMember(interfaceMethod)); - var synthesizedExplicitImpl = classC.GetSynthesizedExplicitImplementations(CancellationToken.None).Single(); + var synthesizedExplicitImpl = classC.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Single(); Assert.Equal(classC, synthesizedExplicitImpl.ContainingType); Assert.Equal(interfaceMethod, synthesizedExplicitImpl.ExplicitInterfaceImplementations.Single()); Assert.Equal(classBMethod, synthesizedExplicitImpl.ImplementingMethod); @@ -1712,7 +1712,7 @@ class C : B, I { } Assert.Equal(classBMethod, classC.FindImplementationForInterfaceMember(interfaceMethod)); - Assert.Equal(0, classC.GetSynthesizedExplicitImplementations(CancellationToken.None).Length); + Assert.Equal(0, classC.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Length); } [Fact] @@ -1897,7 +1897,7 @@ class D : B, I comp2.VerifyDiagnostics(); var derivedType = comp2.GlobalNamespace.GetMember("D"); - var bridgeMethod = derivedType.GetSynthesizedExplicitImplementations(CancellationToken.None).Single(); + var bridgeMethod = derivedType.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Single(); Assert.Equal("NonVirtual", bridgeMethod.ImplementingMethod.Name); } @@ -2033,7 +2033,7 @@ public class D : B, I Assert.Equal(RefKind.Ref, baseMethod.RefKind); Assert.Equal(baseMethod, derivedType.FindImplementationForInterfaceMember(interfaceMethod)); - var synthesized = derivedType.GetSynthesizedExplicitImplementations(CancellationToken.None).Single(); + var synthesized = derivedType.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Single(); Assert.Equal(baseMethod, synthesized.ImplementingMethod); Assert.Equal(interfaceMethod, synthesized.ExplicitInterfaceImplementations.Single()); @@ -2705,7 +2705,7 @@ private void ExplicitImplementationInBaseType( CompileAndVerify(comp, expectedOutput: expectedOutput); var derivedType = comp.GetMember(derivedTypeName); - Assert.True(derivedType.GetSynthesizedExplicitImplementations(cancellationToken: default).IsEmpty); + Assert.True(derivedType.GetSynthesizedExplicitImplementations(cancellationToken: default).ForwardingMethods.IsEmpty); var interfaceMember = comp.GetMember(interfaceMemberName); var implementingMember = derivedType.FindImplementationForInterfaceMember(interfaceMember); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs index 607ce03a4b0dc..2e2f22eeef666 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs @@ -326,5 +326,10 @@ internal override AttributeUsageInfo GetAttributeUsageInfo() internal override bool IsRecord => false; internal override bool HasPossibleWellKnownCloneMethod() => false; + + internal sealed override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() + { + return SpecializedCollections.EmptyEnumerable<(MethodSymbol Body, MethodSymbol Implemented)>(); + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/OverriddenOrHiddenMembersTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/OverriddenOrHiddenMembersTests.cs index 897be18e1d5ac..ba49ee78ce9e3 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/OverriddenOrHiddenMembersTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/OverriddenOrHiddenMembersTests.cs @@ -2025,7 +2025,7 @@ class B2 : B1, I1 "; var comp = CreateCompilation(text); comp.VerifyDiagnostics( - // (10,16): error CS0736: 'B2' does not implement interface member 'I1.Goo'. 'B2.Goo' cannot implement an interface member because it is static. + // (10,16): error CS0736: 'B2' does not implement instance interface member 'I1.Goo'. 'B2.Goo' cannot implement the interface member because it is static. // class B2 : B1, I1 Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberStatic, "I1").WithArguments("B2", "I1.Goo", "B2.Goo").WithLocation(10, 16)); @@ -2064,10 +2064,10 @@ class B3 : I public static void M() { } }"; CreateCompilationWithILAndMscorlib40(csharpSource, ilSource).VerifyDiagnostics( - // (5,15): error CS0736: 'B2' does not implement interface member 'I.M()'. 'A.M()' cannot implement an interface member because it is static. + // (5,15): error CS0736: 'B2' does not implement instance interface member 'I.M()'. 'A.M()' cannot implement the interface member because it is static. // class B2 : A, I Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberStatic, "I").WithArguments("B2", "I.M()", "A.M()").WithLocation(5, 15), - // (8,12): error CS0736: 'B3' does not implement interface member 'I.M()'. 'B3.M()' cannot implement an interface member because it is static. + // (8,12): error CS0736: 'B3' does not implement instance interface member 'I.M()'. 'B3.M()' cannot implement the interface member because it is static. // class B3 : I Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberStatic, "I").WithArguments("B3", "I.M()", "B3.M()").WithLocation(8, 12)); } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/CustomModifierCopyTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/CustomModifierCopyTests.cs index dbf8e882f5f52..135c35ba5d853 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/CustomModifierCopyTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/CustomModifierCopyTests.cs @@ -62,7 +62,7 @@ public void Method2(int x) { } AssertNoParameterHasModOpts(classMethod2); // bridge method for implicit implementation has custom modifiers - var method2ExplicitImpl = @class.GetSynthesizedExplicitImplementations(CancellationToken.None).Single(); + var method2ExplicitImpl = @class.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Single(); Assert.Same(classMethod2, method2ExplicitImpl.ImplementingMethod); AssertAllParametersHaveConstModOpt(method2ExplicitImpl); } @@ -109,7 +109,7 @@ public void Method2(int x) { } AssertNoParameterHasModOpts(classMethod2); // bridge methods for implicit implementation have custom modifiers - var method2ExplicitImpls = @class.GetSynthesizedExplicitImplementations(CancellationToken.None); + var method2ExplicitImpls = @class.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods; Assert.Equal(2, method2ExplicitImpls.Length); foreach (var explicitImpl in method2ExplicitImpls) { @@ -156,7 +156,7 @@ public override void VirtualMethod(int x) { } AssertNoParameterHasModOpts(classNonVirtualMethod); // no bridge methods - Assert.Equal(0, @class.GetSynthesizedExplicitImplementations(CancellationToken.None).Length); + Assert.Equal(0, @class.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Length); } /// @@ -205,7 +205,7 @@ public override void NonVirtualMethod(int x) { } AssertNoParameterHasModOpts(baseClassNonVirtualMethod); // no bridge methods - Assert.Equal(0, baseClass.GetSynthesizedExplicitImplementations(CancellationToken.None).Length); + Assert.Equal(0, baseClass.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Length); var derivedClass = global.GetMember("Derived"); @@ -218,7 +218,7 @@ public override void NonVirtualMethod(int x) { } AssertNoParameterHasModOpts(derivedClassNonVirtualMethod); // no bridge methods - Assert.Equal(0, derivedClass.GetSynthesizedExplicitImplementations(CancellationToken.None).Length); + Assert.Equal(0, derivedClass.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Length); } /// @@ -406,7 +406,7 @@ class Class3 : CppCli.CppBase2, CppCli.CppInterface1 var class1 = global.GetMember("Class1"); //both implementations are from the base class - var class1SynthesizedExplicitImpls = class1.GetSynthesizedExplicitImplementations(CancellationToken.None); + var class1SynthesizedExplicitImpls = class1.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods; Assert.Equal(1, class1SynthesizedExplicitImpls.Length); //Don't need a bridge method for the virtual base method. foreach (var explicitImpl in class1SynthesizedExplicitImpls) { @@ -421,7 +421,7 @@ class Class3 : CppCli.CppBase2, CppCli.CppInterface1 AssertAllParametersHaveConstModOpt(class2Method1); //Method2 is implemented in the base class - var class2Method2SynthesizedExplicitImpl = class2.GetSynthesizedExplicitImplementations(CancellationToken.None).Single(); + var class2Method2SynthesizedExplicitImpl = class2.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Single(); Assert.Equal("Method2", class2Method2SynthesizedExplicitImpl.ExplicitInterfaceImplementations.Single().Name); Assert.Same(baseClass, class2Method2SynthesizedExplicitImpl.ImplementingMethod.ContainingType); AssertAllParametersHaveConstModOpt(class2Method2SynthesizedExplicitImpl); @@ -434,7 +434,7 @@ class Class3 : CppCli.CppBase2, CppCli.CppInterface1 // GetSynthesizedExplicitImplementations doesn't guarantee order, so sort to make the asserts easier to write. - var class3SynthesizedExplicitImpls = (from m in class3.GetSynthesizedExplicitImplementations(CancellationToken.None) orderby m.Name select m).ToArray(); + var class3SynthesizedExplicitImpls = (from m in class3.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods orderby m.Name select m).ToArray(); Assert.Equal(2, class3SynthesizedExplicitImpls.Length); var class3Method1SynthesizedExplicitImpl = class3SynthesizedExplicitImpls[0]; @@ -482,7 +482,7 @@ void I2.M1(int x) { } } //no bridge methods - Assert.False(@class.GetSynthesizedExplicitImplementations(CancellationToken.None).Any()); + Assert.False(@class.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Any()); } /// @@ -567,7 +567,7 @@ int CppCli.CppIndexerInterface.this[int x] var classIndexer = (PropertySymbol)@class.GetMembers().Where(s => s.Kind == SymbolKind.Property).Single(); AssertAllParametersHaveConstModOpt(classIndexer); - Assert.Equal(0, @class.GetSynthesizedExplicitImplementations(CancellationToken.None).Length); + Assert.Equal(0, @class.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Length); } /// @@ -601,7 +601,7 @@ public int this[int x] AssertNoParameterHasModOpts(classIndexer); // bridge methods for implicit implementations have custom modifiers - var explicitImpls = @class.GetSynthesizedExplicitImplementations(CancellationToken.None); + var explicitImpls = @class.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods; Assert.Equal(2, explicitImpls.Length); var explicitGetterImpl = explicitImpls.Where(impl => impl.ImplementingMethod.MethodKind == MethodKind.PropertyGet).Single(); @@ -641,7 +641,7 @@ public override int this[int x] var classIndexer = (PropertySymbol)@class.GetMembers().Where(s => s.Kind == SymbolKind.Property).Single(); AssertAllParametersHaveConstModOpt(classIndexer); - Assert.Equal(0, @class.GetSynthesizedExplicitImplementations(CancellationToken.None).Length); + Assert.Equal(0, @class.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods.Length); } /// diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index 975815d2a72d5..535987b94cce4 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -12,8 +12,10 @@ using System.Threading; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; +using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; @@ -4163,7 +4165,7 @@ void validate(ModuleSymbol module) { var m01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); - Assert.True(m01.IsMetadataNewSlot()); + Assert.False(m01.IsMetadataNewSlot()); Assert.True(m01.IsAbstract); Assert.True(m01.IsMetadataVirtual()); Assert.False(m01.IsMetadataFinal); @@ -4231,7 +4233,7 @@ void validate(ModuleSymbol module) { var m01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); - Assert.True(m01.IsMetadataNewSlot()); + Assert.False(m01.IsMetadataNewSlot()); Assert.True(m01.IsAbstract); Assert.True(m01.IsMetadataVirtual()); Assert.False(m01.IsMetadataFinal); @@ -4268,7 +4270,7 @@ void validate(ModuleSymbol module) int count = 0; foreach (var m01 in module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType()) { - Assert.True(m01.IsMetadataNewSlot()); + Assert.False(m01.IsMetadataNewSlot()); Assert.True(m01.IsAbstract); Assert.True(m01.IsMetadataVirtual()); Assert.False(m01.IsMetadataFinal); @@ -4391,7 +4393,7 @@ void validate(ModuleSymbol module) int count = 0; foreach (var m01 in module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType()) { - Assert.True(m01.IsMetadataNewSlot()); + Assert.False(m01.IsMetadataNewSlot()); Assert.True(m01.IsAbstract); Assert.True(m01.IsMetadataVirtual()); Assert.False(m01.IsMetadataFinal); @@ -4460,7 +4462,7 @@ void validate(ModuleSymbol module) int count = 0; foreach (var m01 in module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType()) { - Assert.True(m01.IsMetadataNewSlot()); + Assert.False(m01.IsMetadataNewSlot()); Assert.True(m01.IsAbstract); Assert.True(m01.IsMetadataVirtual()); Assert.False(m01.IsMetadataFinal); @@ -4926,6 +4928,9 @@ interface I13 // (44,35): error CS9102: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator +(T10 x); Diagnostic(ErrorCode.ERR_BadAbstractUnaryOperatorSignature, op).WithLocation(44, 35), + // (47,18): error CS0535: 'C11' does not implement interface member 'I10.operator false(T11)' + // class C11 : I10 where T11 : C11 {} + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I10").WithArguments("C11", "I10.operator " + op + "(T11)").WithLocation(47, 18), // (51,35): error CS9102: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator false(int x); Diagnostic(ErrorCode.ERR_BadAbstractUnaryOperatorSignature, op).WithLocation(51, 35) @@ -5019,6 +5024,9 @@ interface I13 // (44,34): error CS9103: The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. // static abstract T10 operator ++(T10 x); Diagnostic(ErrorCode.ERR_BadAbstractIncDecSignature, op).WithLocation(44, 34), + // (47,18): error CS0535: 'C11' does not implement interface member 'I10.operator --(T11)' + // class C11 : I10 where T11 : C11 {} + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I10").WithArguments("C11", "I10.operator " + op + "(T11)").WithLocation(47, 18), // (51,34): error CS9103: The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. // static abstract int operator ++(int x); Diagnostic(ErrorCode.ERR_BadAbstractIncDecSignature, op).WithLocation(51, 34) @@ -5126,6 +5134,9 @@ interface I15 where T151 : I15 where T152 : I15 x); Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(44, 34), + // (47,18): error CS0535: 'C11' does not implement interface member 'I10.operator ++(I10)' + // class C11 : I10 where T11 : C11 {} + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I10").WithArguments("C11", "I10.operator " + op + "(I10)").WithLocation(47, 18), // (51,34): error CS9104: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. // static abstract int operator ++(I12 x); Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(51, 34), @@ -5238,6 +5249,9 @@ interface I13 // (44,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator +(T10 x, bool y); Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(44, 35), + // (47,18): error CS0535: 'C11' does not implement interface member 'I10.operator /(T11, bool)' + // class C11 : I10 where T11 : C11 {} + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I10").WithArguments("C11", "I10.operator " + op + "(T11, bool)").WithLocation(47, 18), // (51,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator +(int x, bool y); Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(51, 35) @@ -5341,6 +5355,9 @@ interface I13 // (44,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator +(bool y, T10 x); Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(44, 35), + // (47,18): error CS0535: 'C11' does not implement interface member 'I10.operator <=(bool, T11)' + // class C11 : I10 where T11 : C11 {} + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I10").WithArguments("C11", "I10.operator " + op + "(bool, T11)").WithLocation(47, 18), // (51,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator +(bool y, int x); Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(51, 35) @@ -5439,6 +5456,9 @@ interface I14 // (44,35): error CS9106: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int // static abstract bool operator <<(T10 x, int y); Diagnostic(ErrorCode.ERR_BadAbstractShiftOperatorSignature, op).WithLocation(44, 35), + // (47,18): error CS0535: 'C11' does not implement interface member 'I10.operator >>(T11, int)' + // class C11 : I10 where T11 : C11 {} + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I10").WithArguments("C11", "I10.operator " + op + "(T11, int)").WithLocation(47, 18), // (51,35): error CS9106: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int // static abstract bool operator <<(int x, int y); Diagnostic(ErrorCode.ERR_BadAbstractShiftOperatorSignature, op).WithLocation(51, 35), @@ -10946,5 +10966,1581 @@ static void M02() where T : I1 Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "9.0", "preview").WithLocation(12, 26) ); } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticMethod_01(bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} + +" + typeKeyword + @" + C1 : I1 +{} + +" + typeKeyword + @" + C2 : I1 +{ + public void M01() {} +} + +" + typeKeyword + @" + C3 : I1 +{ + static void M01() {} +} + +" + typeKeyword + @" + C4 : I1 +{ + void I1.M01() {} +} + +" + typeKeyword + @" + C5 : I1 +{ + public static int M01() => throw null; +} + +" + typeKeyword + @" + C6 : I1 +{ + static int I1.M01() => throw null; +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (8,10): error CS0535: 'C1' does not implement interface member 'I1.M01()' + // C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01()").WithLocation(8, 10), + // (12,10): error CS9109: 'C2' does not implement static interface member 'I1.M01()'. 'C2.M01()' cannot implement the interface member because it is not static. + // C2 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotStatic, "I1").WithArguments("C2", "I1.M01()", "C2.M01()").WithLocation(12, 10), + // (18,10): error CS0737: 'C3' does not implement interface member 'I1.M01()'. 'C3.M01()' cannot implement an interface member because it is not public. + // C3 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotPublic, "I1").WithArguments("C3", "I1.M01()", "C3.M01()").WithLocation(18, 10), + // (24,10): error CS0535: 'C4' does not implement interface member 'I1.M01()' + // C4 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C4", "I1.M01()").WithLocation(24, 10), + // (26,13): error CS0539: 'C4.M01()' in explicit interface declaration is not found among members of the interface that can be implemented + // void I1.M01() {} + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("C4.M01()").WithLocation(26, 13), + // (30,10): error CS0738: 'C5' does not implement interface member 'I1.M01()'. 'C5.M01()' cannot implement 'I1.M01()' because it does not have the matching return type of 'void'. + // C5 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberWrongReturnType, "I1").WithArguments("C5", "I1.M01()", "C5.M01()", "void").WithLocation(30, 10), + // (36,10): error CS0535: 'C6' does not implement interface member 'I1.M01()' + // C6 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C6", "I1.M01()").WithLocation(36, 10), + // (38,19): error CS0539: 'C6.M01()' in explicit interface declaration is not found among members of the interface that can be implemented + // static int I1.M01() => throw null; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("C6.M01()").WithLocation(38, 19) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticMethod_02(bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract void M01(); +} + +" + typeKeyword + @" + C1 : I1 +{} + +" + typeKeyword + @" + C2 : I1 +{ + public static void M01() {} +} + +" + typeKeyword + @" + C3 : I1 +{ + void M01() {} +} + +" + typeKeyword + @" + C4 : I1 +{ + static void I1.M01() {} +} + +" + typeKeyword + @" + C5 : I1 +{ + public int M01() => throw null; +} + +" + typeKeyword + @" + C6 : I1 +{ + int I1.M01() => throw null; +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (8,10): error CS0535: 'C1' does not implement interface member 'I1.M01()' + // C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01()").WithLocation(8, 10), + // (12,10): error CS0736: 'C2' does not implement instance interface member 'I1.M01()'. 'C2.M01()' cannot implement the interface member because it is static. + // C2 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberStatic, "I1").WithArguments("C2", "I1.M01()", "C2.M01()").WithLocation(12, 10), + // (18,10): error CS0737: 'C3' does not implement interface member 'I1.M01()'. 'C3.M01()' cannot implement an interface member because it is not public. + // C3 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotPublic, "I1").WithArguments("C3", "I1.M01()", "C3.M01()").WithLocation(18, 10), + // (24,10): error CS0535: 'C4' does not implement interface member 'I1.M01()' + // C4 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C4", "I1.M01()").WithLocation(24, 10), + // (26,20): error CS0539: 'C4.M01()' in explicit interface declaration is not found among members of the interface that can be implemented + // static void I1.M01() {} + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("C4.M01()").WithLocation(26, 20), + // (30,10): error CS0738: 'C5' does not implement interface member 'I1.M01()'. 'C5.M01()' cannot implement 'I1.M01()' because it does not have the matching return type of 'void'. + // C5 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberWrongReturnType, "I1").WithArguments("C5", "I1.M01()", "C5.M01()", "void").WithLocation(30, 10), + // (36,10): error CS0535: 'C6' does not implement interface member 'I1.M01()' + // C6 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C6", "I1.M01()").WithLocation(36, 10), + // (38,12): error CS0539: 'C6.M01()' in explicit interface declaration is not found among members of the interface that can be implemented + // int I1.M01() => throw null; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("C6.M01()").WithLocation(38, 12) + ); + } + + [Fact] + public void ImplementAbstractStaticMethod_03() + { + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} + +interface I2 : I1 +{} + +interface I3 : I1 +{ + public virtual void M01() {} +} + +interface I4 : I1 +{ + static void M01() {} +} + +interface I5 : I1 +{ + void I1.M01() {} +} + +interface I6 : I1 +{ + static void I1.M01() {} +} + +interface I7 : I1 +{ + abstract static void M01(); +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (12,25): warning CS0108: 'I3.M01()' hides inherited member 'I1.M01()'. Use the new keyword if hiding was intended. + // public virtual void M01() {} + Diagnostic(ErrorCode.WRN_NewRequired, "M01").WithArguments("I3.M01()", "I1.M01()").WithLocation(12, 25), + // (17,17): warning CS0108: 'I4.M01()' hides inherited member 'I1.M01()'. Use the new keyword if hiding was intended. + // static void M01() {} + Diagnostic(ErrorCode.WRN_NewRequired, "M01").WithArguments("I4.M01()", "I1.M01()").WithLocation(17, 17), + // (22,13): error CS0539: 'I5.M01()' in explicit interface declaration is not found among members of the interface that can be implemented + // void I1.M01() {} + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("I5.M01()").WithLocation(22, 13), + // (27,20): error CS0106: The modifier 'static' is not valid for this item + // static void I1.M01() {} + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M01").WithArguments("static").WithLocation(27, 20), + // (27,20): error CS0539: 'I6.M01()' in explicit interface declaration is not found among members of the interface that can be implemented + // static void I1.M01() {} + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("I6.M01()").WithLocation(27, 20), + // (32,26): warning CS0108: 'I7.M01()' hides inherited member 'I1.M01()'. Use the new keyword if hiding was intended. + // abstract static void M01(); + Diagnostic(ErrorCode.WRN_NewRequired, "M01").WithArguments("I7.M01()", "I1.M01()").WithLocation(32, 26) + ); + + var m01 = compilation1.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); + + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I2").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I3").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I4").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I5").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I6").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I7").FindImplementationForInterfaceMember(m01)); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticMethod_04(bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} +"; + var source2 = +typeKeyword + @" + Test: I1 +{ + static void I1.M01() {} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (4,20): error CS8703: The modifier 'static' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // static void I1.M01() {} + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("static", "9.0", "preview").WithLocation(4, 20) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation3.VerifyDiagnostics( + // (4,20): error CS8703: The modifier 'static' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // static void I1.M01() {} + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("static", "9.0", "preview").WithLocation(4, 20), + // (9,26): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static void M01(); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "9.0", "preview").WithLocation(9, 26) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticMethod_05(bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} +"; + var source2 = +typeKeyword + @" + Test1: I1 +{ + public static void M01() {} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (2,12): error CS9110: 'Test1.M01()' cannot implement interface member 'I1.M01()' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01()", "I1.M01()", "Test1").WithLocation(2, 12) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation3.VerifyDiagnostics( + // (2,12): error CS9110: 'Test1.M01()' cannot implement interface member 'I1.M01()' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01()", "I1.M01()", "Test1").WithLocation(2, 12), + // (9,26): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static void M01(); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(9, 26) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticMethod_06(bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} +"; + var source2 = +typeKeyword + @" + Test1: I1 +{ + static void I1.M01() {} +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (4,20): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // static void I1.M01() {} + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(4, 20) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation3.VerifyDiagnostics( + // (4,20): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // static void I1.M01() {} + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(4, 20), + // (9,26): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static void M01(); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(9, 26) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticMethod_07(bool structure) + { + // Basic implicit implementation scenario, MethodImpl is emitted + + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} + +" + typeKeyword + @" + C : I1 +{ + public static void M01() {} +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped, + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false)).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var m01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); + var c = module.GlobalNamespace.GetTypeMember("C"); + + var cM01 = (MethodSymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + Assert.False(cM01.IsMetadataVirtual()); + Assert.False(cM01.IsMetadataFinal); + Assert.False(cM01.IsMetadataNewSlot()); + + Assert.Equal("void C.M01()", cM01.ToTestDisplayString()); + + if (module is PEModuleSymbol) + { + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(cM01.ExplicitInterfaceImplementations); + } + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticMethod_08(bool structure) + { + // Basic explicit implementation scenario + + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} + +" + typeKeyword + @" + C : I1 +{ + static void I1.M01() {} +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped, + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false)).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var m01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); + var c = module.GlobalNamespace.GetTypeMember("C"); + + var cM01 = (MethodSymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + Assert.False(cM01.IsMetadataVirtual()); + Assert.False(cM01.IsMetadataFinal); + Assert.False(cM01.IsMetadataNewSlot()); + + Assert.Equal("void C.I1.M01()", cM01.ToTestDisplayString()); + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + } + } + + [Fact] + public void ImplementAbstractStaticMethod_09() + { + // Explicit implementation from base is treated as an implementation + + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} + +public class C1 +{ + public static void M01() {} +} + +public class C2 : C1, I1 +{ + static void I1.M01() {} +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C3 : C2, I1 +{ +} +"; + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.EmitToImageReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + var m01 = c3.Interfaces().Single().GetMembers().OfType().Single(); + + var cM01 = (MethodSymbol)c3.FindImplementationForInterfaceMember(m01); + + Assert.Equal("void C2.I1.M01()", cM01.ToTestDisplayString()); + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + } + } + + [Fact] + public void ImplementAbstractStaticMethod_10() + { + // Implicit implementation is considered only for types implementing interface in source. + // In metadata, only explicit implementations are considered + + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig static abstract virtual + void M01 () cil managed + { + } // end of method I1::M01 +} // end of class I1 + +.class public auto ansi beforefieldinit C1 + extends System.Object + implements I1 +{ + .method private hidebysig + static void I1.M01 () cil managed + { + .override method void I1::M01() + .maxstack 8 + + IL_0000: ret + } // end of method C1::I1.M01 + + .method public hidebysig static + void M01 () cil managed + { + IL_0000: ret + } // end of method C1::M01 + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + IL_0000: ldarg.0 + IL_0001: call instance void System.Object::.ctor() + IL_0006: ret + } // end of method C1::.ctor +} // end of class C1 + +.class public auto ansi beforefieldinit C2 + extends C1 + implements I1 +{ + .method public hidebysig static + void M01 () cil managed + { + IL_0000: ret + } // end of method C2::M01 + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + IL_0000: ldarg.0 + IL_0001: call instance void C1::.ctor() + IL_0006: ret + } // end of method C2::.ctor +} // end of class C2 +"; + var source1 = +@" +public class C3 : C2 +{ +} + +public class C4 : C1, I1 +{ +} + +public class C5 : C2, I1 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); + + var c1M01 = (MethodSymbol)c1.FindImplementationForInterfaceMember(m01); + + Assert.Equal("void C1.I1.M01()", c1M01.ToTestDisplayString()); + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + + var c2 = compilation1.GlobalNamespace.GetTypeMember("C2"); + Assert.Same(c1M01, c2.FindImplementationForInterfaceMember(m01)); + + var c3 = compilation1.GlobalNamespace.GetTypeMember("C3"); + Assert.Same(c1M01, c3.FindImplementationForInterfaceMember(m01)); + + var c4 = compilation1.GlobalNamespace.GetTypeMember("C4"); + Assert.Same(c1M01, c4.FindImplementationForInterfaceMember(m01)); + + var c5 = compilation1.GlobalNamespace.GetTypeMember("C5"); + + Assert.Equal("void C2.M01()", c5.FindImplementationForInterfaceMember(m01).ToTestDisplayString()); + } + + [Fact] + public void ImplementAbstractStaticMethod_11() + { + // Ignore invalid metadata (non-abstract static virtual method). + + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig virtual + static void M01 () cil managed + { + IL_0000: ret + } // end of method I1::M01 +} // end of class I1 +"; + + var source1 = +@" +public class C1 : I1 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyEmitDiagnostics(); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var i1 = c1.Interfaces().Single(); + var m01 = i1.GetMembers().OfType().Single(); + + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + var source2 = +@" +public class C1 : I1 +{ + static void I1.M01() {} +} +"; + + var compilation2 = CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation2.VerifyEmitDiagnostics( + // (4,19): error CS0539: 'C1.M01()' in explicit interface declaration is not found among members of the interface that can be implemented + // static void I1.M01() {} + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("C1.M01()").WithLocation(4, 19) + ); + + c1 = compilation2.GlobalNamespace.GetTypeMember("C1"); + m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); + + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + } + + [Fact] + public void ImplementAbstractStaticMethod_12() + { + // Ignore invalid metadata (default interface implementation for a static method) + + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig abstract virtual + static void M01 () cil managed + { + } // end of method I1::M01 +} // end of class I1 +.class interface public auto ansi abstract I2 + implements I1 +{ + // Methods + .method private hidebysig + static void I1.M01 () cil managed + { + .override method void I1::M01() + IL_0000: ret + } // end of method I2::I1.M01 +} // end of class I2 +"; + + var source1 = +@" +public class C1 : I2 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyEmitDiagnostics( + // (2,19): error CS0535: 'C1' does not implement interface member 'I1.M01()' + // public class C1 : I2 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I2").WithArguments("C1", "I1.M01()").WithLocation(2, 19) + ); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var i2 = c1.Interfaces().Single(); + var i1 = i2.Interfaces().Single(); + var m01 = i1.GetMembers().OfType().Single(); + + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(i2.FindImplementationForInterfaceMember(m01)); + } + + [Fact] + public void ImplementAbstractStaticMethod_13() + { + // A forwarding method is added for an implicit implementation declared in base class. + + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} + +class C1 +{ + public static void M01() {} +} + +class C2 : C1, I1 +{ +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var m01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + + var c2M01 = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01); + + Assert.True(c2M01.IsStatic); + Assert.False(c2M01.IsAbstract); + Assert.False(c2M01.IsVirtual); + Assert.False(c2M01.IsMetadataVirtual()); + Assert.False(c2M01.IsMetadataFinal); + Assert.False(c2M01.IsMetadataNewSlot()); + + if (module is PEModuleSymbol) + { + Assert.Equal("void C2.I1.M01()", c2M01.ToTestDisplayString()); + Assert.Same(m01, c2M01.ExplicitInterfaceImplementations.Single()); + + var c1M01 = module.GlobalNamespace.GetMember("C1.M01"); + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + Assert.False(c1M01.IsMetadataVirtual()); + Assert.False(c1M01.IsMetadataFinal); + Assert.False(c1M01.IsMetadataNewSlot()); + } + else + { + Assert.Equal("void C1.M01()", c2M01.ToTestDisplayString()); + Assert.Empty(c2M01.ExplicitInterfaceImplementations); + } + } + + verifier.VerifyIL("C2.I1.M01()", +@" +{ + // Code size 6 (0x6) + .maxstack 0 + IL_0000: call ""void C1.M01()"" + IL_0005: ret +} +"); + } + + [Fact] + public void ImplementAbstractStaticMethod_14() + { + // A forwarding method is added for an implicit implementation with modopt mismatch. + + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig abstract virtual + static void modopt(I1) M01 () cil managed + { + } // end of method I1::M01 +} // end of class I1 +"; + + var source1 = +@" +class C1 : I1 +{ + public static void M01() {} +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c1 = module.GlobalNamespace.GetTypeMember("C1"); + var m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); + + var c1M01 = (MethodSymbol)c1.FindImplementationForInterfaceMember(m01); + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + Assert.False(c1M01.IsMetadataVirtual()); + Assert.False(c1M01.IsMetadataFinal); + Assert.False(c1M01.IsMetadataNewSlot()); + + if (module is PEModuleSymbol) + { + Assert.Equal("void modopt(I1) C1.I1.M01()", c1M01.ToTestDisplayString()); + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + + c1M01 = module.GlobalNamespace.GetMember("C1.M01"); + Assert.Equal("void C1.M01()", c1M01.ToTestDisplayString()); + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + Assert.False(c1M01.IsMetadataVirtual()); + Assert.False(c1M01.IsMetadataFinal); + Assert.False(c1M01.IsMetadataNewSlot()); + + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + } + else + { + Assert.Equal("void C1.M01()", c1M01.ToTestDisplayString()); + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + } + } + + verifier.VerifyIL("C1.I1.M01()", +@" +{ + // Code size 6 (0x6) + .maxstack 0 + IL_0000: call ""void C1.M01()"" + IL_0005: ret +} +"); + } + + [Fact] + public void ImplementAbstractStaticMethod_15() + { + // A forwarding method isn't created if base class implements interface exactly the same way. + + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} + +public class C1 +{ + public static void M01() {} +} + +public class C2 : C1, I1 +{ +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C3 : C2, I1 +{ +} +"; + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.EmitToImageReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + var m01 = c3.Interfaces().Single().GetMembers().OfType().Single(); + + var c1M01 = c3.BaseType().BaseType().GetMember("M01"); + Assert.Equal("void C1.M01()", c1M01.ToTestDisplayString()); + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + Assert.False(c1M01.IsMetadataVirtual()); + Assert.False(c1M01.IsMetadataFinal); + Assert.False(c1M01.IsMetadataNewSlot()); + + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + + if (c1M01.ContainingModule is PEModuleSymbol) + { + var c2M01 = (MethodSymbol)c3.FindImplementationForInterfaceMember(m01); + Assert.Equal("void C2.I1.M01()", c2M01.ToTestDisplayString()); + Assert.Same(m01, c2M01.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Same(c1M01, c3.FindImplementationForInterfaceMember(m01)); + } + } + } + + [Fact] + public void ImplementAbstractStaticMethod_16() + { + // A new implicit implementation is properly considered. + + var source1 = +@" +public interface I1 +{ + abstract static void M01(); +} + +public class C1 : I1 +{ + public static void M01() {} +} + +public class C2 : C1 +{ + new public static void M01() {} +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C3 : C2, I1 +{ +} +"; + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + var verifier = CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("C3.I1.M01()", +@" +{ + // Code size 6 (0x6) + .maxstack 0 + IL_0000: call ""void C2.M01()"" + IL_0005: ret +} +"); + + compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.EmitToImageReference() }); + + verifier = CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("C3.I1.M01()", +@" +{ + // Code size 6 (0x6) + .maxstack 0 + IL_0000: call ""void C2.M01()"" + IL_0005: ret +} +"); + + void validate(ModuleSymbol module) + { + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + var m01 = c3.Interfaces().Single().GetMembers().OfType().Single(); + + var c2M01 = c3.BaseType().GetMember("M01"); + Assert.Equal("void C2.M01()", c2M01.ToTestDisplayString()); + + Assert.True(c2M01.IsStatic); + Assert.False(c2M01.IsAbstract); + Assert.False(c2M01.IsVirtual); + Assert.False(c2M01.IsMetadataVirtual()); + Assert.False(c2M01.IsMetadataFinal); + Assert.False(c2M01.IsMetadataNewSlot()); + + Assert.Empty(c2M01.ExplicitInterfaceImplementations); + + if (module is PEModuleSymbol) + { + var c3M01 = (MethodSymbol)c3.FindImplementationForInterfaceMember(m01); + Assert.Equal("void C3.I1.M01()", c3M01.ToTestDisplayString()); + Assert.Same(m01, c3M01.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Same(c2M01, c3.FindImplementationForInterfaceMember(m01)); + } + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticMethod_17(bool genericFirst) + { + // An "ambiguity" in implicit implementation declared in generic base class + + var generic = +@" + public static void M01(T x) {} +"; + var nonGeneric = +@" + public static void M01(int x) {} +"; + var source1 = +@" +public interface I1 +{ + abstract static void M01(int x); +} + +public class C1 : I1 +{ +" + (genericFirst ? generic + nonGeneric : nonGeneric + generic) + @" +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { CreateCompilation("", targetFramework: TargetFramework.NetCoreApp).ToMetadataReference() }); + + Assert.Equal(2, compilation1.GlobalNamespace.GetTypeMember("C1").GetMembers().Where(m => m.Name.Contains("M01")).Count()); + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C2 : C1, I1 +{ +} +"; + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.EmitToImageReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + var m01 = c2.Interfaces().Single().GetMembers().OfType().Single(); + + Assert.True(m01.ContainingModule is RetargetingModuleSymbol or PEModuleSymbol); + + var c1M01 = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01); + Assert.Equal("void C1.M01(System.Int32 x)", c1M01.OriginalDefinition.ToTestDisplayString()); + + var baseI1M01 = c2.BaseType().FindImplementationForInterfaceMember(m01); + Assert.Equal("void C1.M01(System.Int32 x)", baseI1M01.OriginalDefinition.ToTestDisplayString()); + + Assert.Equal(c1M01, baseI1M01); + + if (c1M01.OriginalDefinition.ContainingModule is PEModuleSymbol) + { + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + } + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticMethod_18(bool genericFirst) + { + // An "ambiguity" in implicit implementation declared in generic base class plus interface is generic too. + + var generic = +@" + public static void M01(T x) {} +"; + var nonGeneric = +@" + public static void M01(int x) {} +"; + var source1 = +@" +public interface I1 +{ + abstract static void M01(T x); +} + +public class C1 : I1 +{ +" + (genericFirst ? generic + nonGeneric : nonGeneric + generic) + @" +} +"; + + + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { CreateCompilation("", targetFramework: TargetFramework.NetCoreApp).ToMetadataReference() }); + + Assert.Equal(2, compilation1.GlobalNamespace.GetTypeMember("C1").GetMembers().Where(m => m.Name.Contains("M01")).Count()); + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C2 : C1, I1 +{ +} +"; + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.EmitToImageReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + var m01 = c2.Interfaces().Single().GetMembers().OfType().Single(); + + Assert.True(m01.ContainingModule is RetargetingModuleSymbol or PEModuleSymbol); + + var c1M01 = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01); + Assert.Equal("void C1.M01(T x)", c1M01.OriginalDefinition.ToTestDisplayString()); + + var baseI1M01 = c2.BaseType().FindImplementationForInterfaceMember(m01); + Assert.Equal("void C1.M01(T x)", baseI1M01.OriginalDefinition.ToTestDisplayString()); + + Assert.Equal(c1M01, baseI1M01); + + if (c1M01.OriginalDefinition.ContainingModule is PEModuleSymbol) + { + Assert.Equal(m01, c1M01.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + } + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticMethod_19(bool genericFirst) + { + // Same as ImplementAbstractStaticMethod_17 only implementation is explicit in source. + + var generic = +@" + public static void M01(T x) {} +"; + var nonGeneric = +@" + static void I1.M01(int x) {} +"; + var source1 = +@" +public interface I1 +{ + abstract static void M01(int x); +} + +public class C1 : I1 +{ +" + (genericFirst ? generic + nonGeneric : nonGeneric + generic) + @" +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { CreateCompilation("", targetFramework: TargetFramework.NetCoreApp).ToMetadataReference() }); + + Assert.Equal(2, compilation1.GlobalNamespace.GetTypeMember("C1").GetMembers().Where(m => m.Name.Contains("M01")).Count()); + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C2 : C1, I1 +{ +} +"; + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.EmitToImageReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + var m01 = c2.Interfaces().Single().GetMembers().OfType().Single(); + + Assert.True(m01.ContainingModule is RetargetingModuleSymbol or PEModuleSymbol); + + var c1M01 = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01); + Assert.Equal("void C1.I1.M01(System.Int32 x)", c1M01.OriginalDefinition.ToTestDisplayString()); + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(c1M01, c2.BaseType().FindImplementationForInterfaceMember(m01)); + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticMethod_20(bool genericFirst) + { + // Same as ImplementAbstractStaticMethod_18 only implementation is explicit in source. + + var generic = +@" + static void I1.M01(T x) {} +"; + var nonGeneric = +@" + public static void M01(int x) {} +"; + var source1 = +@" +public interface I1 +{ + abstract static void M01(T x); +} + +public class C1 : I1 +{ +" + (genericFirst ? generic + nonGeneric : nonGeneric + generic) + @" +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { CreateCompilation("", targetFramework: TargetFramework.NetCoreApp).ToMetadataReference() }); + + Assert.Equal(2, compilation1.GlobalNamespace.GetTypeMember("C1").GetMembers().Where(m => m.Name.Contains("M01")).Count()); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C2 : C1, I1 +{ +} +"; + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.EmitToImageReference() }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + var m01 = c2.Interfaces().Single().GetMembers().OfType().Single(); + + Assert.True(m01.ContainingModule is RetargetingModuleSymbol or PEModuleSymbol); + + var c1M01 = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01); + Assert.Equal("void C1.I1.M01(T x)", c1M01.OriginalDefinition.ToTestDisplayString()); + Assert.Equal(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(c1M01, c2.BaseType().FindImplementationForInterfaceMember(m01)); + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticMethod_21(bool genericFirst) + { + // Same as ImplementAbstractStaticMethod_17 only implicit implementation is in an intermediate base. + + var generic = +@" + public static void M01(T x) {} +"; + var nonGeneric = +@" + public static void M01(int x) {} +"; + var source1 = +@" +public interface I1 +{ + abstract static void M01(int x); +} + +public class C1 +{ +" + (genericFirst ? generic + nonGeneric : nonGeneric + generic) + @" +} + +public class C11 : C1, I1 +{ +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { CreateCompilation("", targetFramework: TargetFramework.NetCoreApp).ToMetadataReference() }); + + Assert.Equal(2, compilation1.GlobalNamespace.GetTypeMember("C1").GetMembers().Where(m => m.Name.Contains("M01")).Count()); + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C2 : C11, I1 +{ +} +"; + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.EmitToImageReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + var m01 = c2.Interfaces().Single().GetMembers().OfType().Single(); + + Assert.True(m01.ContainingModule is RetargetingModuleSymbol or PEModuleSymbol); + + var c1M01 = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01); + var expectedDisplay = m01.ContainingModule is PEModuleSymbol ? "void C11.I1.M01(System.Int32 x)" : "void C1.M01(System.Int32 x)"; + Assert.Equal(expectedDisplay, c1M01.OriginalDefinition.ToTestDisplayString()); + + var baseI1M01 = c2.BaseType().FindImplementationForInterfaceMember(m01); + Assert.Equal(expectedDisplay, baseI1M01.OriginalDefinition.ToTestDisplayString()); + + Assert.Equal(c1M01, baseI1M01); + + if (c1M01.OriginalDefinition.ContainingModule is PEModuleSymbol) + { + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + } + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticMethod_22(bool genericFirst) + { + // Same as ImplementAbstractStaticMethod_18 only implicit implementation is in an intermediate base. + + var generic = +@" + public static void M01(T x) {} +"; + var nonGeneric = +@" + public static void M01(int x) {} +"; + var source1 = +@" +public interface I1 +{ + abstract static void M01(T x); +} + +public class C1 +{ +" + (genericFirst ? generic + nonGeneric : nonGeneric + generic) + @" +} + +public class C11 : C1, I1 +{ +} +"; + + + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { CreateCompilation("", targetFramework: TargetFramework.NetCoreApp).ToMetadataReference() }); + + Assert.Equal(2, compilation1.GlobalNamespace.GetTypeMember("C1").GetMembers().Where(m => m.Name.Contains("M01")).Count()); + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C2 : C11, I1 +{ +} +"; + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.EmitToImageReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + var m01 = c2.Interfaces().Single().GetMembers().OfType().Single(); + + Assert.True(m01.ContainingModule is RetargetingModuleSymbol or PEModuleSymbol); + + var c1M01 = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01); + var expectedDisplay = m01.ContainingModule is PEModuleSymbol ? "void C11.I1.M01(T x)" : "void C1.M01(T x)"; + Assert.Equal(expectedDisplay, c1M01.OriginalDefinition.ToTestDisplayString()); + + var baseI1M01 = c2.BaseType().FindImplementationForInterfaceMember(m01); + Assert.Equal(expectedDisplay, baseI1M01.OriginalDefinition.ToTestDisplayString()); + + Assert.Equal(c1M01, baseI1M01); + + if (c1M01.OriginalDefinition.ContainingModule is PEModuleSymbol) + { + Assert.Equal(m01, c1M01.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + } + } + } } } diff --git a/src/Compilers/Core/Portable/MetadataReader/ModuleExtensions.cs b/src/Compilers/Core/Portable/MetadataReader/ModuleExtensions.cs index ed245a450a224..1e72ed326be3e 100644 --- a/src/Compilers/Core/Portable/MetadataReader/ModuleExtensions.cs +++ b/src/Compilers/Core/Portable/MetadataReader/ModuleExtensions.cs @@ -78,7 +78,7 @@ public static bool ShouldImportField(FieldAttributes flags, MetadataImportOption /// explicit interface implementations. For other methods, visibility and the value of /// are considered. /// - public static bool ShouldImportMethod(this PEModule module, MethodDefinitionHandle methodDef, MetadataImportOptions importOptions) + public static bool ShouldImportMethod(this PEModule module, TypeDefinitionHandle typeDef, MethodDefinitionHandle methodDef, MetadataImportOptions importOptions) { try { @@ -87,27 +87,11 @@ public static bool ShouldImportMethod(this PEModule module, MethodDefinitionHand // If the method is virtual, it must be accessible, although // it may be an explicit (private) interface implementation. // Otherwise, we need to check the accessibility. - if ((flags & MethodAttributes.Virtual) == 0) + if ((flags & MethodAttributes.Virtual) == 0 && !acceptBasedOnAccessibility(importOptions, flags) && + ((flags & MethodAttributes.Static) == 0 || !isMethodImpl(typeDef, methodDef))) { - switch (flags & MethodAttributes.MemberAccessMask) - { - case MethodAttributes.Private: - case MethodAttributes.PrivateScope: - if (importOptions != MetadataImportOptions.All) - { - return false; - } - - break; - - case MethodAttributes.Assembly: - if (importOptions == MetadataImportOptions.Public) - { - return false; - } - - break; - } + + return false; } } catch (BadImageFormatException) @@ -127,6 +111,45 @@ public static bool ShouldImportMethod(this PEModule module, MethodDefinitionHand { return true; } + + static bool acceptBasedOnAccessibility(MetadataImportOptions importOptions, MethodAttributes flags) + { + switch (flags & MethodAttributes.MemberAccessMask) + { + case MethodAttributes.Private: + case MethodAttributes.PrivateScope: + if (importOptions != MetadataImportOptions.All) + { + return false; + } + + break; + + case MethodAttributes.Assembly: + if (importOptions == MetadataImportOptions.Public) + { + return false; + } + + break; + } + + return true; + } + + bool isMethodImpl(TypeDefinitionHandle typeDef, MethodDefinitionHandle methodDef) + { + foreach (var methodImpl in module.GetMethodImplementationsOrThrow(typeDef)) + { + module.GetMethodImplPropsOrThrow(methodImpl, out EntityHandle body, out _); + if (body == methodDef) + { + return true; + } + } + + return false; + } } /// diff --git a/src/Compilers/Core/Portable/PEWriter/Members.cs b/src/Compilers/Core/Portable/PEWriter/Members.cs index 70382dde13c75..fcb78a61af057 100644 --- a/src/Compilers/Core/Portable/PEWriter/Members.cs +++ b/src/Compilers/Core/Portable/PEWriter/Members.cs @@ -992,15 +992,36 @@ public static bool ShouldInclude(this ITypeDefinitionMember member, EmitContext return true; } + bool acceptBasedOnVisibility = true; + switch (member.Visibility) { case TypeMemberVisibility.Private: - return context.IncludePrivateMembers; + acceptBasedOnVisibility = context.IncludePrivateMembers; + break; case TypeMemberVisibility.Assembly: case TypeMemberVisibility.FamilyAndAssembly: - return context.IncludePrivateMembers || context.Module.SourceAssemblyOpt?.InternalsAreVisible == true; + acceptBasedOnVisibility = context.IncludePrivateMembers || context.Module.SourceAssemblyOpt?.InternalsAreVisible == true; + break; + } + + if (acceptBasedOnVisibility) + { + return true; } - return true; + + if (method?.IsStatic == true) + { + foreach (var methodImplementation in method.ContainingTypeDefinition.GetExplicitImplementationOverrides(context)) + { + if (methodImplementation.ImplementingMethod == method) + { + return true; + } + } + } + + return false; } } } diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.vb index 02e4d5e75dc07..8d6930537561a 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.vb @@ -1187,7 +1187,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Try For Each methodDef In [module].GetMethodsOfTypeOrThrow(_handle) - If [module].ShouldImportMethod(methodDef, moduleSymbol.ImportOptions) Then + If [module].ShouldImportMethod(_handle, methodDef, moduleSymbol.ImportOptions) Then methods.Add(methodDef, New PEMethodSymbol(moduleSymbol, Me, methodDef)) End If Next @@ -1207,8 +1207,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Dim methods = [module].GetPropertyMethodsOrThrow(propertyDef) - Dim getMethod = GetAccessorMethod(moduleSymbol, methodHandleToSymbol, methods.Getter) - Dim setMethod = GetAccessorMethod(moduleSymbol, methodHandleToSymbol, methods.Setter) + Dim getMethod = GetAccessorMethod(moduleSymbol, methodHandleToSymbol, _handle, methods.Getter) + Dim setMethod = GetAccessorMethod(moduleSymbol, methodHandleToSymbol, _handle, methods.Setter) If (getMethod IsNot Nothing) OrElse (setMethod IsNot Nothing) Then members.Add(PEPropertySymbol.Create(moduleSymbol, Me, propertyDef, getMethod, setMethod)) @@ -1230,9 +1230,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Dim methods = [module].GetEventMethodsOrThrow(eventRid) ' NOTE: C# ignores all other accessors (most notably, raise/fire). - Dim addMethod = GetAccessorMethod(moduleSymbol, methodHandleToSymbol, methods.Adder) - Dim removeMethod = GetAccessorMethod(moduleSymbol, methodHandleToSymbol, methods.Remover) - Dim raiseMethod = GetAccessorMethod(moduleSymbol, methodHandleToSymbol, methods.Raiser) + Dim addMethod = GetAccessorMethod(moduleSymbol, methodHandleToSymbol, _handle, methods.Adder) + Dim removeMethod = GetAccessorMethod(moduleSymbol, methodHandleToSymbol, _handle, methods.Remover) + Dim raiseMethod = GetAccessorMethod(moduleSymbol, methodHandleToSymbol, _handle, methods.Raiser) ' VB ignores events that do not have both Add and Remove. If (addMethod IsNot Nothing) AndAlso (removeMethod IsNot Nothing) Then @@ -1245,14 +1245,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE End Try End Sub - Private Shared Function GetAccessorMethod(moduleSymbol As PEModuleSymbol, methodHandleToSymbol As Dictionary(Of MethodDefinitionHandle, PEMethodSymbol), methodDef As MethodDefinitionHandle) As PEMethodSymbol + Private Shared Function GetAccessorMethod(moduleSymbol As PEModuleSymbol, methodHandleToSymbol As Dictionary(Of MethodDefinitionHandle, PEMethodSymbol), typeDef As TypeDefinitionHandle, methodDef As MethodDefinitionHandle) As PEMethodSymbol If methodDef.IsNil Then Return Nothing End If Dim method As PEMethodSymbol = Nothing Dim found As Boolean = methodHandleToSymbol.TryGetValue(methodDef, method) - Debug.Assert(found OrElse Not moduleSymbol.Module.ShouldImportMethod(methodDef, moduleSymbol.ImportOptions)) + Debug.Assert(found OrElse Not moduleSymbol.Module.ShouldImportMethod(typeDef, methodDef, moduleSymbol.ImportOptions)) Return method End Function diff --git a/src/Compilers/VisualBasic/Test/Semantic/Semantics/UserDefinedBinaryOperators.vb b/src/Compilers/VisualBasic/Test/Semantic/Semantics/UserDefinedBinaryOperators.vb index 496041616321f..7b7ddd5713b60 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Semantics/UserDefinedBinaryOperators.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Semantics/UserDefinedBinaryOperators.vb @@ -910,7 +910,7 @@ op_BitwiseOr End Sub - Public Sub OperatorMapping_BothSignedAndUnsignedShift() + Public Sub OperatorMapping_BothSignedAndUnsignedShift_01() Dim ilSource = > 2 << 3 + End Sub +End Module + ]]> + + + Dim compilation = CompilationUtils.CreateCompilationWithCustomILSource(compilationDef, ilSource.Value, includeVbRuntime:=True, options:=TestOptions.ReleaseExe) + + Dim verifier = CompileAndVerify(compilation, + expectedOutput:= + ) + End Sub + + + Public Sub OperatorMapping_BothSignedAndUnsignedShift_02() + + Dim ilSource = + + + Dim compilationDef = + + SynthesizedInterfaceMethodImpls() + { + return SpecializedCollections.EmptyEnumerable<(MethodSymbol Body, MethodSymbol Implemented)>(); + } } } From c6e79d66b1b26e659fad3b83d3a983d76d087b2a Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Mon, 3 May 2021 12:37:11 -0700 Subject: [PATCH 058/127] Update based on PR feedback --- .../ValueTrackingService.Visitor.cs | 12 ++ .../ValueTracking/ValueTrackingService.cs | 45 ++---- .../ValueTracking/CSharpValueTrackingTests.cs | 138 ++++++++++++++++++ .../VisualBasicValueTrackingTests.cs | 119 +++++++++++++++ 4 files changed, 280 insertions(+), 34 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs index 3685899ffa88c..b874cd7e162d5 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs @@ -40,11 +40,23 @@ IFieldReferenceOperation or IPropertyReferenceOperation => VisitReferenceAsync(operation, cancellationToken), IAssignmentOperation assignmentOperation => VisitAssignmentOperationAsync(assignmentOperation, cancellationToken), + IMethodBodyOperation methodBodyOperation => VisitReturnDescendentsAsync(methodBodyOperation, allowImplicit: true, cancellationToken), + IBlockOperation blockOperation => VisitReturnDescendentsAsync(blockOperation, allowImplicit: false, cancellationToken), // Default to reporting if there is symbol information available _ => VisitDefaultAsync(operation, cancellationToken) }; + private async Task VisitReturnDescendentsAsync(IOperation operation, bool allowImplicit, CancellationToken cancellationToken) + { + var returnOperations = operation.Descendants().Where(d => d is IReturnOperation && (allowImplicit || !d.IsImplicit)); + foreach (var returnOperation in returnOperations) + { + cancellationToken.ThrowIfCancellationRequested(); + await VisitAsync(returnOperation, cancellationToken).ConfigureAwait(false); + } + } + private async Task VisitDefaultAsync(IOperation operation, CancellationToken cancellationToken) { // If an operation has children, desend in them by default. diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 85ac49dfa3082..26e6fb0b35acd 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -243,46 +243,23 @@ private static async Task TrackMethodSymbolAsync(IMethodSymbol methodSymbol, Ope var node = location.FindNode(cancellationToken); var sourceDoc = collector.Solution.GetRequiredDocument(location.SourceTree); var syntaxFacts = sourceDoc.GetRequiredLanguageService(); - var returnStatements = node.DescendantNodesAndSelf().Where(n => syntaxFacts.IsReturnStatement(n)).ToImmutableArray(); var semanticModel = await sourceDoc.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - if (returnStatements.IsDefaultOrEmpty) - { - // If there are no return statements and the method has a return type, then the method body is an expression - // and we're interested in parsing that expression - var expression = node.DescendantNodesAndSelf().First(syntaxFacts.IsMethodBody); - if (expression is null) - { - return; - } - - var operation = semanticModel.GetOperation(expression, cancellationToken); - if (operation is null) - { - continue; - } + var operation = semanticModel.GetOperation(node, cancellationToken); - await collector.VisitAsync(operation, cancellationToken).ConfigureAwait(false); + // In VB the parent node contains the operation (IBlockOperation) instead of the one returned + // by the symbol location. + if (operation is null && node.Parent is not null) + { + operation = semanticModel.GetOperation(node.Parent, cancellationToken); } - else + + if (operation is null) { - foreach (var returnStatement in returnStatements) - { - var expression = syntaxFacts.GetExpressionOfReturnStatement(returnStatement); - if (expression is null) - { - continue; - } - - var operation = semanticModel.GetOperation(expression, cancellationToken); - if (operation is null) - { - continue; - } - - await collector.VisitAsync(operation, cancellationToken).ConfigureAwait(false); - } + continue; } + + await collector.VisitAsync(operation, cancellationToken).ConfigureAwait(false); } } diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs index 1239a722d4272..19174c8d90ef0 100644 --- a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -800,5 +800,143 @@ public static int GetM() await ValidateChildrenEmptyAsync(workspace, items.Single()); } + + [Fact] + public async Task TestVariableReferenceStart3() + { + var code = +@" +class Test +{ + public static void M() + { + int x = GetM(); + Console.Write($$x); + var y = x + 1; + x += 1; + Console.Write(x); + Console.Write(y); + } + + public static int GetM() + { + var x = 0; + return x; + } +}"; + + // + // |> Console.Write(x); [Code.cs:6] + // |> int x = GetM() [Code.cs:5] + // |> return x; [Code.cs:13] + // |> var x = 0; [Code.cs:12] + // |> x += 1 [Code.cs:8] + using var workspace = TestWorkspace.CreateCSharp(code); + + var items = await ValidateItemsAsync( + workspace, + itemInfo: new[] + { + (6, "x") // |> Console.Write([|x|]); [Code.cs:7] + }); + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (8, "1"), // |> x += 1; [Codec.s:8] + (5, "GetM()"), // |> int x = [|GetM()|] [Code.cs:5] + }); + + await ValidateChildrenEmptyAsync(workspace, items[0]); + + items = await ValidateChildrenAsync( + workspace, + items[1], + childInfo: new[] + { + (16, "x") // |> return [|x|]; [Code.cs:13] + }); + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (15, "0") // |> var x = [|0|]; [Code.cs:12] + }); + + await ValidateChildrenEmptyAsync(workspace, items.Single()); + } + + [Fact] + public async Task TestMultipleDeclarators() + { + var code = +@" +class Test +{ + public static void M() + { + int x = GetM(), z = 5; + Console.Write($$x); + var y = x + 1 + z; + x += 1; + Console.Write(x); + Console.Write(y); + } + + public static int GetM() + { + var x = 0; + return x; + } +}"; + + // + // |> Console.Write(x); [Code.cs:6] + // |> int x = GetM() [Code.cs:5] + // |> return x; [Code.cs:13] + // |> var x = 0; [Code.cs:12] + // |> x += 1 [Code.cs:8] + using var workspace = TestWorkspace.CreateCSharp(code); + + var items = await ValidateItemsAsync( + workspace, + itemInfo: new[] + { + (6, "x") // |> Console.Write([|x|]); [Code.cs:7] + }); + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (8, "1"), // |> x += 1; [Codec.s:8] + (5, "GetM()"), // |> int x = [|GetM()|] [Code.cs:5] + }); + + await ValidateChildrenEmptyAsync(workspace, items[0]); + + items = await ValidateChildrenAsync( + workspace, + items[1], + childInfo: new[] + { + (16, "x") // |> return [|x|]; [Code.cs:13] + }); + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (15, "0") // |> var x = [|0|]; [Code.cs:12] + }); + + await ValidateChildrenEmptyAsync(workspace, items.Single()); + } } } diff --git a/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs index b6e5e35d115a5..d9b1a6d8368a6 100644 --- a/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Test.Utilities; @@ -139,5 +140,123 @@ End Class ValidateItem(initialItems[0], 3); ValidateItem(initialItems[1], 2); } + + [Fact] + public async Task TestVariableReferenceStart() + { + var code = +@" +Class Test + Public Sub M() + Dim x = GetM() + Console.Write(x) + Dim y = $$x + 1 + End Sub + + Public Function GetM() As Integer + Dim x = 0 + Return x + End Function +End Class"; + + // + // |> Dim y = x + 1 [Code.cs:7] + // |> Dim x = GetM() [Code.cs:5] + // |> Return x; [Code.cs:13] + // |> Dim x = 0; [Code.cs:12] + using var workspace = TestWorkspace.CreateVisualBasic(code); + + var items = await ValidateItemsAsync( + workspace, + itemInfo: new[] + { + (5, "x") // |> Dim y = [|x|] + 1; [Code.cs:7] + }); + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (3, "GetM()") // |> Dim x = [|GetM()|] [Code.cs:5] + }); + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (10, "x") // |> return [|x|]; [Code.cs:13] + }); + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (9, "0") // |> var x = [|0|]; [Code.cs:12] + }); + + await ValidateChildrenEmptyAsync(workspace, items.Single()); + } + + [Fact] + public async Task TestVariableReferenceStart2() + { + var code = +@" +Class Test + Public Sub M() + Dim x = GetM() + Console.Write($$x) + Dim y = x + 1 + End Sub + + Public Function GetM() As Integer + Dim x = 0 + Return x + End Function +End Class"; + + // + // |> Dim y = x + 1 [Code.cs:7] + // |> Dim x = GetM() [Code.cs:5] + // |> Return x; [Code.cs:13] + // |> Dim x = 0; [Code.cs:12] + using var workspace = TestWorkspace.CreateVisualBasic(code); + + var items = await ValidateItemsAsync( + workspace, + itemInfo: new[] + { + (4, "x") // |> Dim y = [|x|] + 1; [Code.cs:7] + }); + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (3, "GetM()") // |> Dim x = [|GetM()|] [Code.cs:5] + }); + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (10, "x") // |> return [|x|]; [Code.cs:13] + }); + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (9, "0") // |> var x = [|0|]; [Code.cs:12] + }); + + await ValidateChildrenEmptyAsync(workspace, items.Single()); + } } } From 3a42e1a3dc85a80d6170635a0eed96ef1a4e91c7 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Mon, 3 May 2021 12:51:10 -0700 Subject: [PATCH 059/127] Add multiple declarator tests --- .../VisualBasicValueTrackingTests.cs | 93 +++++++++++++++---- 1 file changed, 77 insertions(+), 16 deletions(-) diff --git a/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs index d9b1a6d8368a6..24c24ca9cc16c 100644 --- a/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs @@ -160,17 +160,17 @@ End Function End Class"; // - // |> Dim y = x + 1 [Code.cs:7] - // |> Dim x = GetM() [Code.cs:5] - // |> Return x; [Code.cs:13] - // |> Dim x = 0; [Code.cs:12] + // |> Dim y = x + 1 [Code.vb:7] + // |> Dim x = GetM() [Code.vb:5] + // |> Return x; [Code.vb:13] + // |> Dim x = 0; [Code.vb:12] using var workspace = TestWorkspace.CreateVisualBasic(code); var items = await ValidateItemsAsync( workspace, itemInfo: new[] { - (5, "x") // |> Dim y = [|x|] + 1; [Code.cs:7] + (5, "x") // |> Dim y = [|x|] + 1; [Code.vb:7] }); items = await ValidateChildrenAsync( @@ -178,7 +178,7 @@ End Function items.Single(), childInfo: new[] { - (3, "GetM()") // |> Dim x = [|GetM()|] [Code.cs:5] + (3, "GetM()") // |> Dim x = [|GetM()|] [Code.vb:5] }); items = await ValidateChildrenAsync( @@ -186,7 +186,7 @@ End Function items.Single(), childInfo: new[] { - (10, "x") // |> return [|x|]; [Code.cs:13] + (10, "x") // |> return [|x|]; [Code.vb:13] }); items = await ValidateChildrenAsync( @@ -194,7 +194,7 @@ End Function items.Single(), childInfo: new[] { - (9, "0") // |> var x = [|0|]; [Code.cs:12] + (9, "0") // |> var x = [|0|]; [Code.vb:12] }); await ValidateChildrenEmptyAsync(workspace, items.Single()); @@ -219,17 +219,17 @@ End Function End Class"; // - // |> Dim y = x + 1 [Code.cs:7] - // |> Dim x = GetM() [Code.cs:5] - // |> Return x; [Code.cs:13] - // |> Dim x = 0; [Code.cs:12] + // |> Dim y = x + 1 [Code.vb:7] + // |> Dim x = GetM() [Code.vb:5] + // |> Return x; [Code.vb:13] + // |> Dim x = 0; [Code.vb:12] using var workspace = TestWorkspace.CreateVisualBasic(code); var items = await ValidateItemsAsync( workspace, itemInfo: new[] { - (4, "x") // |> Dim y = [|x|] + 1; [Code.cs:7] + (4, "x") // |> Dim y = [|x|] + 1; [Code.vb:7] }); items = await ValidateChildrenAsync( @@ -237,7 +237,7 @@ End Function items.Single(), childInfo: new[] { - (3, "GetM()") // |> Dim x = [|GetM()|] [Code.cs:5] + (3, "GetM()") // |> Dim x = [|GetM()|] [Code.vb:5] }); items = await ValidateChildrenAsync( @@ -245,7 +245,7 @@ End Function items.Single(), childInfo: new[] { - (10, "x") // |> return [|x|]; [Code.cs:13] + (10, "x") // |> return [|x|]; [Code.vb:13] }); items = await ValidateChildrenAsync( @@ -253,7 +253,68 @@ End Function items.Single(), childInfo: new[] { - (9, "0") // |> var x = [|0|]; [Code.cs:12] + (9, "0") // |> var x = [|0|]; [Code.vb:12] + }); + + await ValidateChildrenEmptyAsync(workspace, items.Single()); + } + + [Fact] + public async Task TestMultipleDeclarators() + { + var code = +@" +Imports System + +Class Test + Public Sub M() + Dim x = GetM(), z = 1, m As Boolean, n As Boolean, o As Boolean + Console.Write(x) + Dim y = $$x + 1 + End Sub + + Public Function GetM() As Integer + Dim x = 0 + Return x + End Function +End Class"; + + // + // |> Dim y = x + 1 [Code.vb:7] + // |> Dim x = GetM(), z = 1, m As Boolean, n As Boolean, o As Boolean [Code.vb:5] + // |> Return x; [Code.vb:12] + // |> Dim x = 0; [Code.vb:11] + using var workspace = TestWorkspace.CreateVisualBasic(code); + + var items = await ValidateItemsAsync( + workspace, + itemInfo: new[] + { + (7, "x") // |> Dim y = [|x|] + 1; [Code.vb:7] + }); + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (5, "GetM()") // |> Dim x = [|GetM()|], z = 1, m As Boolean, n As Boolean, o As Boolean [Code.vb:5] + }); + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (12, "x") // |> return [|x|]; [Code.vb:12] + }); + + items = await ValidateChildrenAsync( + workspace, + items.Single(), + childInfo: new[] + { + (11, "0") // |> var x = [|0|]; [Code.vb:11] }); await ValidateChildrenEmptyAsync(workspace, items.Single()); From de19e26ca4db8c377016810bc69548ae5284dd61 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Mon, 3 May 2021 14:06:01 -0700 Subject: [PATCH 060/127] Introduce a common base type for SourceOrdinaryMethodSymbolBase and SourceUserDefinedOperatorSymbolBase to prepare for explicit implementations of operators (#53058) Shared code is pulled from derived types to the new base. This is just a refactoring, no behavior changes intended. However, there is a change in behavior that addresses #53069. --- ...dinaryMethodOrUserDefinedOperatorSymbol.cs | 275 ++++++++++++++++++ .../Source/SourceOrdinaryMethodSymbolBase.cs | 259 +---------------- .../SourceUserDefinedOperatorSymbolBase.cs | 102 ++----- .../Attributes/AttributeTests_Nullable.cs | 6 + 4 files changed, 308 insertions(+), 334 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs new file mode 100644 index 0000000000000..1d3ba796909c0 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs @@ -0,0 +1,275 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Emit; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + internal abstract class SourceOrdinaryMethodOrUserDefinedOperatorSymbol : SourceMemberMethodSymbol + { + private ImmutableArray _lazyExplicitInterfaceImplementations; + private ImmutableArray _lazyRefCustomModifiers; + private ImmutableArray _lazyParameters; + private TypeWithAnnotations _lazyReturnType; + + protected SourceOrdinaryMethodOrUserDefinedOperatorSymbol(NamedTypeSymbol containingType, SyntaxReference syntaxReferenceOpt, Location location, bool isIterator) + : base(containingType, syntaxReferenceOpt, location, isIterator) + { + } + + protected abstract Location ReturnTypeLocation { get; } + + public sealed override bool ReturnsVoid + { + get + { + LazyMethodChecks(); + return base.ReturnsVoid; + } + } + + protected MethodSymbol? MethodChecks(TypeWithAnnotations returnType, ImmutableArray parameters, BindingDiagnosticBag diagnostics) + { + _lazyReturnType = returnType; + _lazyParameters = parameters; + + // set ReturnsVoid flag + this.SetReturnsVoid(_lazyReturnType.IsVoidType()); + + this.CheckEffectiveAccessibility(_lazyReturnType, _lazyParameters, diagnostics); + + var location = locations[0]; + // Checks taken from MemberDefiner::defineMethod + if (this.Name == WellKnownMemberNames.DestructorName && this.ParameterCount == 0 && this.Arity == 0 && this.ReturnsVoid) + { + diagnostics.Add(ErrorCode.WRN_FinalizeMethod, location); + } + + ExtensionMethodChecks(diagnostics); + + if (IsPartial) + { + if (MethodKind == MethodKind.ExplicitInterfaceImplementation) + { + diagnostics.Add(ErrorCode.ERR_PartialMethodNotExplicit, location); + } + + if (!ContainingType.IsPartial()) + { + diagnostics.Add(ErrorCode.ERR_PartialMethodOnlyInPartialClass, location); + } + } + + if (!IsPartial) + { + LazyAsyncMethodChecks(CancellationToken.None); + Debug.Assert(state.HasComplete(CompletionPart.FinishAsyncMethodChecks)); + } + + // The runtime will not treat this method as an override or implementation of another + // method unless both the signatures and the custom modifiers match. Hence, in the + // case of overrides and *explicit* implementations, we need to copy the custom modifiers + // that are in the signature of the overridden/implemented method. (From source, we know + // that there can only be one such method, so there are no conflicts.) This is + // unnecessary for implicit implementations because, if the custom modifiers don't match, + // we'll insert a bridge method (an explicit implementation that delegates to the implicit + // implementation) with the correct custom modifiers + // (see SourceMemberContainerTypeSymbol.SynthesizeInterfaceMemberImplementation). + + // This value may not be correct, but we need something while we compute this.OverriddenMethod. + // May be re-assigned below. + Debug.Assert(_lazyReturnType.CustomModifiers.IsEmpty); + _lazyRefCustomModifiers = ImmutableArray.Empty; + + MethodSymbol? overriddenOrExplicitlyImplementedMethod = null; + + // Note: we're checking if the syntax indicates explicit implementation rather, + // than if explicitInterfaceType is null because we don't want to look for an + // overridden property if this is supposed to be an explicit implementation. + if (MethodKind != MethodKind.ExplicitInterfaceImplementation) + { + Debug.Assert(_lazyExplicitInterfaceImplementations.IsDefault); + _lazyExplicitInterfaceImplementations = ImmutableArray.Empty; + + // If this method is an override, we may need to copy custom modifiers from + // the overridden method (so that the runtime will recognize it as an override). + // We check for this case here, while we can still modify the parameters and + // return type without losing the appearance of immutability. + if (this.IsOverride) + { + // This computation will necessarily be performed with partially incomplete + // information. There is no way we can determine the complete signature + // (i.e. including custom modifiers) until we have found the method that + // this method overrides. To accommodate this, MethodSymbol.OverriddenOrHiddenMembers + // is written to allow relaxed matching of custom modifiers for source methods, + // on the assumption that they will be updated appropriately. + overriddenOrExplicitlyImplementedMethod = this.OverriddenMethod; + + if ((object)overriddenOrExplicitlyImplementedMethod != null) + { + CustomModifierUtils.CopyMethodCustomModifiers(overriddenOrExplicitlyImplementedMethod, this, out _lazyReturnType, + out _lazyRefCustomModifiers, + out _lazyParameters, alsoCopyParamsModifier: true); + } + } + else if (RefKind == RefKind.RefReadOnly) + { + var modifierType = Binder.GetWellKnownType(DeclaringCompilation, WellKnownType.System_Runtime_InteropServices_InAttribute, diagnostics, ReturnTypeLocation); + + _lazyRefCustomModifiers = ImmutableArray.Create(CSharpCustomModifier.CreateRequired(modifierType)); + } + } + else if (ExplicitInterfaceType is not null) + { + //do this last so that it can assume the method symbol is constructed (except for ExplicitInterfaceImplementation) + overriddenOrExplicitlyImplementedMethod = FindExplicitlyImplementedMethod(diagnostics); + + if (overriddenOrExplicitlyImplementedMethod is not null) + { + Debug.Assert(_lazyExplicitInterfaceImplementations.IsDefault); + _lazyExplicitInterfaceImplementations = ImmutableArray.Create(overriddenOrExplicitlyImplementedMethod); + + CustomModifierUtils.CopyMethodCustomModifiers(overriddenOrExplicitlyImplementedMethod, this, out _lazyReturnType, + out _lazyRefCustomModifiers, + out _lazyParameters, alsoCopyParamsModifier: false); + this.FindExplicitlyImplementedMemberVerification(overriddenOrExplicitlyImplementedMethod, diagnostics); + TypeSymbol.CheckNullableReferenceTypeMismatchOnImplementingMember(this.ContainingType, this, overriddenOrExplicitlyImplementedMethod, isExplicit: true, diagnostics); + } + else + { + Debug.Assert(_lazyExplicitInterfaceImplementations.IsDefault); + _lazyExplicitInterfaceImplementations = ImmutableArray.Empty; + + Debug.Assert(_lazyReturnType.CustomModifiers.IsEmpty); + } + } + + return overriddenOrExplicitlyImplementedMethod; + } + + protected abstract void ExtensionMethodChecks(BindingDiagnosticBag diagnostics); + + protected abstract MethodSymbol? FindExplicitlyImplementedMethod(BindingDiagnosticBag diagnostics); + + protected abstract TypeSymbol? ExplicitInterfaceType { get; } + + internal sealed override int ParameterCount + { + get + { + if (!_lazyParameters.IsDefault) + { + int result = _lazyParameters.Length; + Debug.Assert(result == GetParameterCountFromSyntax()); + return result; + } + + return GetParameterCountFromSyntax(); + } + } + + protected abstract int GetParameterCountFromSyntax(); + + public sealed override ImmutableArray Parameters + { + get + { + LazyMethodChecks(); + return _lazyParameters; + } + } + + public sealed override TypeWithAnnotations ReturnTypeWithAnnotations + { + get + { + LazyMethodChecks(); + return _lazyReturnType; + } + } + + internal sealed override bool IsExplicitInterfaceImplementation + { + get + { + return MethodKind == MethodKind.ExplicitInterfaceImplementation; + } + } + + public sealed override ImmutableArray ExplicitInterfaceImplementations + { + get + { + LazyMethodChecks(); + return _lazyExplicitInterfaceImplementations; + } + } + + public sealed override ImmutableArray RefCustomModifiers + { + get + { + LazyMethodChecks(); + return _lazyRefCustomModifiers; + } + } + + internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, BindingDiagnosticBag diagnostics) + { + base.AfterAddingTypeMembersChecks(conversions, diagnostics); + + var location = ReturnTypeLocation; + var compilation = DeclaringCompilation; + + Debug.Assert(location != null); + + // Check constraints on return type and parameters. Note: Dev10 uses the + // method name location for any such errors. We'll do the same for return + // type errors but for parameter errors, we'll use the parameter location. + CheckConstraintsForExplicitInterfaceType(conversions, diagnostics); + + this.ReturnType.CheckAllConstraints(compilation, conversions, this.Locations[0], diagnostics); + + foreach (var parameter in this.Parameters) + { + parameter.Type.CheckAllConstraints(compilation, conversions, parameter.Locations[0], diagnostics); + } + + PartialMethodChecks(diagnostics); + + if (RefKind == RefKind.RefReadOnly) + { + compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); + } + + ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); + + if (ReturnType.ContainsNativeInteger()) + { + compilation.EnsureNativeIntegerAttributeExists(diagnostics, location, modifyCompilation: true); + } + + ParameterHelpers.EnsureNativeIntegerAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); + + if (compilation.ShouldEmitNullableAttributes(this) && ReturnTypeWithAnnotations.NeedsNullableAttribute()) + { + compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); + } + + ParameterHelpers.EnsureNullableAttributeExists(compilation, this, Parameters, diagnostics, modifyCompilation: true); + } + + protected abstract void CheckConstraintsForExplicitInterfaceType(ConversionsBase conversions, BindingDiagnosticBag diagnostics); + + protected abstract void PartialMethodChecks(BindingDiagnosticBag diagnostics); + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs index 4c2bce82cc32a..270c158ef8ab7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs @@ -21,16 +21,11 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols /// on any specific kind of syntax node associated with it. Any syntax node is good enough /// for it. /// - internal abstract class SourceOrdinaryMethodSymbolBase : SourceMemberMethodSymbol + internal abstract class SourceOrdinaryMethodSymbolBase : SourceOrdinaryMethodOrUserDefinedOperatorSymbol { private readonly ImmutableArray _typeParameters; private readonly string _name; - private ImmutableArray _lazyExplicitInterfaceImplementations; - private ImmutableArray _lazyRefCustomModifiers; - private ImmutableArray _lazyParameters; - private TypeWithAnnotations _lazyReturnType; - protected SourceOrdinaryMethodSymbolBase( NamedTypeSymbol containingType, string name, @@ -83,133 +78,14 @@ protected SourceOrdinaryMethodSymbolBase( protected abstract ImmutableArray MakeTypeParameters(CSharpSyntaxNode node, BindingDiagnosticBag diagnostics); - public override bool ReturnsVoid - { - get - { - LazyMethodChecks(); - return base.ReturnsVoid; - } - } - +#nullable enable protected override void MethodChecks(BindingDiagnosticBag diagnostics) { Debug.Assert(this.MethodKind != MethodKind.UserDefinedOperator, "SourceUserDefinedOperatorSymbolBase overrides this"); - ImmutableArray declaredConstraints; - bool isVararg; - (_lazyReturnType, _lazyParameters, isVararg, declaredConstraints) = MakeParametersAndBindReturnType(diagnostics); - - // set ReturnsVoid flag - this.SetReturnsVoid(_lazyReturnType.IsVoidType()); - - this.CheckEffectiveAccessibility(_lazyReturnType, _lazyParameters, diagnostics); - - var location = locations[0]; - // Checks taken from MemberDefiner::defineMethod - if (this.Name == WellKnownMemberNames.DestructorName && this.ParameterCount == 0 && this.Arity == 0 && this.ReturnsVoid) - { - diagnostics.Add(ErrorCode.WRN_FinalizeMethod, location); - } - - ExtensionMethodChecks(diagnostics); - - if (IsPartial) - { - if (MethodKind == MethodKind.ExplicitInterfaceImplementation) - { - diagnostics.Add(ErrorCode.ERR_PartialMethodNotExplicit, location); - } - - if (!ContainingType.IsPartial()) - { - diagnostics.Add(ErrorCode.ERR_PartialMethodOnlyInPartialClass, location); - } - } - - if (!IsPartial) - { - LazyAsyncMethodChecks(CancellationToken.None); - Debug.Assert(state.HasComplete(CompletionPart.FinishAsyncMethodChecks)); - } - - // The runtime will not treat this method as an override or implementation of another - // method unless both the signatures and the custom modifiers match. Hence, in the - // case of overrides and *explicit* implementations, we need to copy the custom modifiers - // that are in the signature of the overridden/implemented method. (From source, we know - // that there can only be one such method, so there are no conflicts.) This is - // unnecessary for implicit implementations because, if the custom modifiers don't match, - // we'll insert a bridge method (an explicit implementation that delegates to the implicit - // implementation) with the correct custom modifiers - // (see SourceMemberContainerTypeSymbol.SynthesizeInterfaceMemberImplementation). - - // This value may not be correct, but we need something while we compute this.OverriddenMethod. - // May be re-assigned below. - Debug.Assert(_lazyReturnType.CustomModifiers.IsEmpty); - _lazyRefCustomModifiers = ImmutableArray.Empty; - - MethodSymbol overriddenOrExplicitlyImplementedMethod = null; - - // Note: we're checking if the syntax indicates explicit implementation rather, - // than if explicitInterfaceType is null because we don't want to look for an - // overridden property if this is supposed to be an explicit implementation. - if (MethodKind != MethodKind.ExplicitInterfaceImplementation) - { - Debug.Assert(_lazyExplicitInterfaceImplementations.IsDefault); - _lazyExplicitInterfaceImplementations = ImmutableArray.Empty; - - // If this method is an override, we may need to copy custom modifiers from - // the overridden method (so that the runtime will recognize it as an override). - // We check for this case here, while we can still modify the parameters and - // return type without losing the appearance of immutability. - if (this.IsOverride) - { - // This computation will necessarily be performed with partially incomplete - // information. There is no way we can determine the complete signature - // (i.e. including custom modifiers) until we have found the method that - // this method overrides. To accommodate this, MethodSymbol.OverriddenOrHiddenMembers - // is written to allow relaxed matching of custom modifiers for source methods, - // on the assumption that they will be updated appropriately. - overriddenOrExplicitlyImplementedMethod = this.OverriddenMethod; - - if ((object)overriddenOrExplicitlyImplementedMethod != null) - { - CustomModifierUtils.CopyMethodCustomModifiers(overriddenOrExplicitlyImplementedMethod, this, out _lazyReturnType, - out _lazyRefCustomModifiers, - out _lazyParameters, alsoCopyParamsModifier: true); - } - } - else if (RefKind == RefKind.RefReadOnly) - { - var modifierType = Binder.GetWellKnownType(DeclaringCompilation, WellKnownType.System_Runtime_InteropServices_InAttribute, diagnostics, ReturnTypeLocation); + var (returnType, parameters, isVararg, declaredConstraints) = MakeParametersAndBindReturnType(diagnostics); - _lazyRefCustomModifiers = ImmutableArray.Create(CSharpCustomModifier.CreateRequired(modifierType)); - } - } - else if ((object)ExplicitInterfaceType != null) - { - //do this last so that it can assume the method symbol is constructed (except for ExplicitInterfaceImplementation) - overriddenOrExplicitlyImplementedMethod = FindExplicitlyImplementedMethod(diagnostics); - - if ((object)overriddenOrExplicitlyImplementedMethod != null) - { - Debug.Assert(_lazyExplicitInterfaceImplementations.IsDefault); - _lazyExplicitInterfaceImplementations = ImmutableArray.Create(overriddenOrExplicitlyImplementedMethod); - - CustomModifierUtils.CopyMethodCustomModifiers(overriddenOrExplicitlyImplementedMethod, this, out _lazyReturnType, - out _lazyRefCustomModifiers, - out _lazyParameters, alsoCopyParamsModifier: false); - this.FindExplicitlyImplementedMemberVerification(overriddenOrExplicitlyImplementedMethod, diagnostics); - TypeSymbol.CheckNullableReferenceTypeMismatchOnImplementingMember(this.ContainingType, this, overriddenOrExplicitlyImplementedMethod, isExplicit: true, diagnostics); - } - else - { - Debug.Assert(_lazyExplicitInterfaceImplementations.IsDefault); - _lazyExplicitInterfaceImplementations = ImmutableArray.Empty; - - Debug.Assert(_lazyReturnType.CustomModifiers.IsEmpty); - } - } + MethodSymbol? overriddenOrExplicitlyImplementedMethod = MethodChecks(returnType, parameters, diagnostics); if (!declaredConstraints.IsDefault && overriddenOrExplicitlyImplementedMethod is object) { @@ -250,19 +126,12 @@ protected override void MethodChecks(BindingDiagnosticBag diagnostics) } } - CheckModifiers(MethodKind == MethodKind.ExplicitInterfaceImplementation, isVararg, HasAnyBody, location, diagnostics); + CheckModifiers(MethodKind == MethodKind.ExplicitInterfaceImplementation, isVararg, HasAnyBody, locations[0], diagnostics); } +#nullable disable protected abstract (TypeWithAnnotations ReturnType, ImmutableArray Parameters, bool IsVararg, ImmutableArray DeclaredConstraintsForOverrideOrImplementation) MakeParametersAndBindReturnType(BindingDiagnosticBag diagnostics); - protected abstract void ExtensionMethodChecks(BindingDiagnosticBag diagnostics); - - protected abstract MethodSymbol FindExplicitlyImplementedMethod(BindingDiagnosticBag diagnostics); - - protected abstract Location ReturnTypeLocation { get; } - - protected abstract TypeSymbol ExplicitInterfaceType { get; } - protected abstract bool HasAnyBody { get; } protected sealed override void LazyAsyncMethodChecks(CancellationToken cancellationToken) @@ -317,69 +186,8 @@ public override ImmutableArray Locations } } - internal sealed override int ParameterCount - { - get - { - if (!_lazyParameters.IsDefault) - { - int result = _lazyParameters.Length; - Debug.Assert(result == GetParameterCountFromSyntax()); - return result; - } - - return GetParameterCountFromSyntax(); - } - } - - protected abstract int GetParameterCountFromSyntax(); - - public sealed override ImmutableArray Parameters - { - get - { - LazyMethodChecks(); - return _lazyParameters; - } - } - - public sealed override TypeWithAnnotations ReturnTypeWithAnnotations - { - get - { - LazyMethodChecks(); - return _lazyReturnType; - } - } - public abstract override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken)); - internal override bool IsExplicitInterfaceImplementation - { - get - { - return MethodKind == MethodKind.ExplicitInterfaceImplementation; - } - } - - public override ImmutableArray ExplicitInterfaceImplementations - { - get - { - LazyMethodChecks(); - return _lazyExplicitInterfaceImplementations; - } - } - - public override ImmutableArray RefCustomModifiers - { - get - { - LazyMethodChecks(); - return _lazyRefCustomModifiers; - } - } - public override string Name { get @@ -566,10 +374,10 @@ private void CheckModifiers(bool isExplicitInterfaceImplementation, bool isVarar // The modifier '{0}' is not valid for this item diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, SyntaxFacts.GetText(SyntaxKind.SealedKeyword)); } - else if (_lazyReturnType.IsStatic) + else if (ReturnType.IsStatic) { // '{0}': static types cannot be used as return types - diagnostics.Add(ErrorFacts.GetStaticClassReturnCode(ContainingType.IsInterfaceType()), location, _lazyReturnType.Type); + diagnostics.Add(ErrorFacts.GetStaticClassReturnCode(ContainingType.IsInterfaceType()), location, ReturnType); } else if (IsAbstract && IsExtern) { @@ -624,7 +432,7 @@ private void CheckModifiers(bool isExplicitInterfaceImplementation, bool isVarar { diagnostics.Add(ErrorCode.ERR_InstanceMemberInStaticClass, location, Name); } - else if (isVararg && (IsGenericMethod || ContainingType.IsGenericType || _lazyParameters.Length > 0 && _lazyParameters[_lazyParameters.Length - 1].IsParams)) + else if (isVararg && (IsGenericMethod || ContainingType.IsGenericType || Parameters.Length > 0 && Parameters[Parameters.Length - 1].IsParams)) { diagnostics.Add(ErrorCode.ERR_BadVarargs, location); } @@ -648,54 +456,5 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor)); } } - - internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, BindingDiagnosticBag diagnostics) - { - base.AfterAddingTypeMembersChecks(conversions, diagnostics); - - var location = ReturnTypeLocation; - var compilation = DeclaringCompilation; - - Debug.Assert(location != null); - - // Check constraints on return type and parameters. Note: Dev10 uses the - // method name location for any such errors. We'll do the same for return - // type errors but for parameter errors, we'll use the parameter location. - CheckConstraintsForExplicitInterfaceType(conversions, diagnostics); - - this.ReturnType.CheckAllConstraints(compilation, conversions, this.Locations[0], diagnostics); - - foreach (var parameter in this.Parameters) - { - parameter.Type.CheckAllConstraints(compilation, conversions, parameter.Locations[0], diagnostics); - } - - PartialMethodChecks(diagnostics); - - if (RefKind == RefKind.RefReadOnly) - { - compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); - } - - ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); - - if (ReturnType.ContainsNativeInteger()) - { - compilation.EnsureNativeIntegerAttributeExists(diagnostics, location, modifyCompilation: true); - } - - ParameterHelpers.EnsureNativeIntegerAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); - - if (compilation.ShouldEmitNullableAttributes(this) && ReturnTypeWithAnnotations.NeedsNullableAttribute()) - { - compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); - } - - ParameterHelpers.EnsureNullableAttributeExists(compilation, this, Parameters, diagnostics, modifyCompilation: true); - } - - protected abstract void CheckConstraintsForExplicitInterfaceType(ConversionsBase conversions, BindingDiagnosticBag diagnostics); - - protected abstract void PartialMethodChecks(BindingDiagnosticBag diagnostics); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs index 40d0a9ef29eb3..d40186e3cd2ea 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs @@ -11,13 +11,11 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { - internal abstract class SourceUserDefinedOperatorSymbolBase : SourceMemberMethodSymbol + internal abstract class SourceUserDefinedOperatorSymbolBase : SourceOrdinaryMethodOrUserDefinedOperatorSymbol { private const TypeCompareKind ComparisonForUserDefinedOperators = TypeCompareKind.IgnoreTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes; private readonly string _name; private readonly bool _isExpressionBodied; - private ImmutableArray _lazyParameters; - private TypeWithAnnotations _lazyReturnType; protected SourceUserDefinedOperatorSymbolBase( MethodKind methodKind, @@ -168,8 +166,6 @@ static void reportModifierIfPresent(DeclarationModifiers result, DeclarationModi } } - protected abstract Location ReturnTypeLocation { get; } - protected (TypeWithAnnotations ReturnType, ImmutableArray Parameters) MakeParametersAndBindReturnType(BaseMethodDeclarationSyntax declarationSyntax, TypeSyntax returnTypeSyntax, BindingDiagnosticBag diagnostics) { TypeWithAnnotations returnType; @@ -226,9 +222,9 @@ static void reportModifierIfPresent(DeclarationModifiers result, DeclarationModi protected override void MethodChecks(BindingDiagnosticBag diagnostics) { - (_lazyReturnType, _lazyParameters) = MakeParametersAndBindReturnType(diagnostics); + var (returnType, parameters) = MakeParametersAndBindReturnType(diagnostics); - this.SetReturnsVoid(_lazyReturnType.IsVoidType()); + MethodChecks(returnType, parameters, diagnostics); // If we have a conversion/equality/inequality operator in an interface or static class then we already // have reported that fact as an error. No need to cascade the error further. @@ -239,16 +235,23 @@ protected override void MethodChecks(BindingDiagnosticBag diagnostics) return; } - // SPEC: All types referenced in an operator declaration must be at least as accessible - // SPEC: as the operator itself. - - CheckEffectiveAccessibility(_lazyReturnType, _lazyParameters, diagnostics); CheckValueParameters(diagnostics); CheckOperatorSignatures(diagnostics); } protected abstract (TypeWithAnnotations ReturnType, ImmutableArray Parameters) MakeParametersAndBindReturnType(BindingDiagnosticBag diagnostics); + protected sealed override void ExtensionMethodChecks(BindingDiagnosticBag diagnostics) + { + } + + protected sealed override MethodSymbol FindExplicitlyImplementedMethod(BindingDiagnosticBag diagnostics) + { + return null; + } + + protected sealed override TypeSymbol ExplicitInterfaceType => null; + private void CheckValueParameters(BindingDiagnosticBag diagnostics) { // SPEC: The parameters of an operator must be value parameters. @@ -663,15 +666,6 @@ public sealed override string Name } } - public sealed override bool ReturnsVoid - { - get - { - LazyMethodChecks(); - return base.ReturnsVoid; - } - } - public sealed override bool IsVararg { get @@ -696,32 +690,6 @@ public sealed override ImmutableArray Locations } } - internal sealed override int ParameterCount - { - get - { - if (!_lazyParameters.IsDefault) - { - int result = _lazyParameters.Length; - Debug.Assert(result == GetParameterCountFromSyntax()); - return result; - } - - return GetParameterCountFromSyntax(); - } - } - - protected abstract int GetParameterCountFromSyntax(); - - public sealed override ImmutableArray Parameters - { - get - { - LazyMethodChecks(); - return _lazyParameters; - } - } - public sealed override ImmutableArray TypeParameters { get { return ImmutableArray.Empty; } @@ -738,51 +706,17 @@ public sealed override RefKind RefKind get { return RefKind.None; } } - public sealed override TypeWithAnnotations ReturnTypeWithAnnotations - { - get - { - LazyMethodChecks(); - return _lazyReturnType; - } - } - internal sealed override bool IsExpressionBodied { get { return _isExpressionBodied; } } - internal sealed override void AfterAddingTypeMembersChecks(ConversionsBase conversions, BindingDiagnosticBag diagnostics) + protected sealed override void CheckConstraintsForExplicitInterfaceType(ConversionsBase conversions, BindingDiagnosticBag diagnostics) { - // Check constraints on return type and parameters. Note: Dev10 uses the - // method name location for any such errors. We'll do the same for return - // type errors but for parameter errors, we'll use the parameter location. - - var compilation = DeclaringCompilation; - - this.ReturnType.CheckAllConstraints(compilation, conversions, this.Locations[0], diagnostics); - - foreach (var parameter in this.Parameters) - { - parameter.Type.CheckAllConstraints(compilation, conversions, parameter.Locations[0], diagnostics); - } - - ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); - - if (ReturnType.ContainsNativeInteger()) - { - compilation.EnsureNativeIntegerAttributeExists(diagnostics, ReturnTypeLocation, modifyCompilation: true); - } - - ParameterHelpers.EnsureNativeIntegerAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); - - if (compilation.ShouldEmitNullableAttributes(this) && - ReturnTypeWithAnnotations.NeedsNullableAttribute()) - { - compilation.EnsureNullableAttributeExists(diagnostics, ReturnTypeLocation, modifyCompilation: true); - } + } - ParameterHelpers.EnsureNullableAttributeExists(compilation, this, Parameters, diagnostics, modifyCompilation: true); + protected sealed override void PartialMethodChecks(BindingDiagnosticBag diagnostics) + { } } } diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs index 7a3bd02a723eb..1bc67f9513729 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs @@ -4321,6 +4321,9 @@ public void ModuleMissingAttribute_OperatorReturnType() // (3,19): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported // public static object? operator+(C a, C b) => null; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "object?").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(3, 19), + // (3,35): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableContextAttribute' is not defined or imported + // public static object? operator+(C a, C b) => null; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "+").WithArguments("System.Runtime.CompilerServices.NullableContextAttribute").WithLocation(3, 35), // (3,37): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported // public static object? operator+(C a, C b) => null; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "C a").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(3, 37), @@ -4343,6 +4346,9 @@ public void ModuleMissingAttribute_OperatorParameters() // (3,19): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported // public static object operator+(C a, object?[] b) => a; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "object").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(3, 19), + // (3,34): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableContextAttribute' is not defined or imported + // public static object operator+(C a, object?[] b) => a; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "+").WithArguments("System.Runtime.CompilerServices.NullableContextAttribute").WithLocation(3, 34), // (3,36): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported // public static object operator+(C a, object?[] b) => a; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "C a").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(3, 36), From 06322f778253010c1c00d2667f0ea62edf1b5bdd Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Mon, 3 May 2021 20:10:03 -0700 Subject: [PATCH 061/127] Support syntax for explicit implementation of operators (#53038) --- .../Portable/Generated/CSharp.Generated.g4 | 2 +- .../Syntax.xml.Internal.Generated.cs | 71 +- .../Syntax.xml.Main.Generated.cs | 12 +- .../Syntax.xml.Syntax.Generated.cs | 54 +- .../CSharp/Portable/Parser/LanguageParser.cs | 189 +- .../CSharp/Portable/PublicAPI.Unshipped.txt | 5 + .../Syntax/OperatorDeclarationSyntax.cs | 37 + .../CSharp/Portable/Syntax/Syntax.xml | 1 + .../CSharp/Portable/Syntax/SyntaxFactory.cs | 47 + .../Generated/Syntax.Test.xml.Generated.cs | 8 +- .../Parsing/MemberDeclarationParsingTests.cs | 1514 +++++++++++++++++ .../Parsing/TopLevelStatementsParsingTests.cs | 5 - 12 files changed, 1844 insertions(+), 101 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/Syntax/OperatorDeclarationSyntax.cs diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index e40f3eaedb827..fe56464459619 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -230,7 +230,7 @@ type_constraint ; operator_declaration - : attribute_list* modifier* type 'operator' ('+' | '-' | '!' | '~' | '++' | '--' | '*' | '/' | '%' | '<<' | '>>' | '|' | '&' | '^' | '==' | '!=' | '<' | '<=' | '>' | '>=' | 'false' | 'true' | 'is') parameter_list (block | (arrow_expression_clause ';')) + : attribute_list* modifier* type explicit_interface_specifier? 'operator' ('+' | '-' | '!' | '~' | '++' | '--' | '*' | '/' | '%' | '<<' | '>>' | '|' | '&' | '^' | '==' | '!=' | '<' | '<=' | '>' | '>=' | 'false' | 'true' | 'is') parameter_list (block | (arrow_expression_clause ';')) ; base_property_declaration diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs index 7a4108972ee84..fd018384cc97b 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs @@ -24226,6 +24226,7 @@ internal sealed partial class OperatorDeclarationSyntax : BaseMethodDeclarationS internal readonly GreenNode? attributeLists; internal readonly GreenNode? modifiers; internal readonly TypeSyntax returnType; + internal readonly ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier; internal readonly SyntaxToken operatorKeyword; internal readonly SyntaxToken operatorToken; internal readonly ParameterListSyntax parameterList; @@ -24233,10 +24234,10 @@ internal sealed partial class OperatorDeclarationSyntax : BaseMethodDeclarationS internal readonly ArrowExpressionClauseSyntax? expressionBody; internal readonly SyntaxToken? semicolonToken; - internal OperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, TypeSyntax returnType, SyntaxToken operatorKeyword, SyntaxToken operatorToken, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) + internal OperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, TypeSyntax returnType, ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, SyntaxToken operatorKeyword, SyntaxToken operatorToken, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) : base(kind, diagnostics, annotations) { - this.SlotCount = 9; + this.SlotCount = 10; if (attributeLists != null) { this.AdjustFlagsAndWidth(attributeLists); @@ -24249,6 +24250,11 @@ internal OperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, G } this.AdjustFlagsAndWidth(returnType); this.returnType = returnType; + if (explicitInterfaceSpecifier != null) + { + this.AdjustFlagsAndWidth(explicitInterfaceSpecifier); + this.explicitInterfaceSpecifier = explicitInterfaceSpecifier; + } this.AdjustFlagsAndWidth(operatorKeyword); this.operatorKeyword = operatorKeyword; this.AdjustFlagsAndWidth(operatorToken); @@ -24272,11 +24278,11 @@ internal OperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, G } } - internal OperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, TypeSyntax returnType, SyntaxToken operatorKeyword, SyntaxToken operatorToken, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken, SyntaxFactoryContext context) + internal OperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, TypeSyntax returnType, ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, SyntaxToken operatorKeyword, SyntaxToken operatorToken, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken, SyntaxFactoryContext context) : base(kind) { this.SetFactoryContext(context); - this.SlotCount = 9; + this.SlotCount = 10; if (attributeLists != null) { this.AdjustFlagsAndWidth(attributeLists); @@ -24289,6 +24295,11 @@ internal OperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, G } this.AdjustFlagsAndWidth(returnType); this.returnType = returnType; + if (explicitInterfaceSpecifier != null) + { + this.AdjustFlagsAndWidth(explicitInterfaceSpecifier); + this.explicitInterfaceSpecifier = explicitInterfaceSpecifier; + } this.AdjustFlagsAndWidth(operatorKeyword); this.operatorKeyword = operatorKeyword; this.AdjustFlagsAndWidth(operatorToken); @@ -24312,10 +24323,10 @@ internal OperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, G } } - internal OperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, TypeSyntax returnType, SyntaxToken operatorKeyword, SyntaxToken operatorToken, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken) + internal OperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, TypeSyntax returnType, ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, SyntaxToken operatorKeyword, SyntaxToken operatorToken, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken) : base(kind) { - this.SlotCount = 9; + this.SlotCount = 10; if (attributeLists != null) { this.AdjustFlagsAndWidth(attributeLists); @@ -24328,6 +24339,11 @@ internal OperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, G } this.AdjustFlagsAndWidth(returnType); this.returnType = returnType; + if (explicitInterfaceSpecifier != null) + { + this.AdjustFlagsAndWidth(explicitInterfaceSpecifier); + this.explicitInterfaceSpecifier = explicitInterfaceSpecifier; + } this.AdjustFlagsAndWidth(operatorKeyword); this.operatorKeyword = operatorKeyword; this.AdjustFlagsAndWidth(operatorToken); @@ -24355,6 +24371,7 @@ internal OperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, G public override Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList Modifiers => new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(this.modifiers); /// Gets the return type. public TypeSyntax ReturnType => this.returnType; + public ExplicitInterfaceSpecifierSyntax? ExplicitInterfaceSpecifier => this.explicitInterfaceSpecifier; /// Gets the "operator" keyword. public SyntaxToken OperatorKeyword => this.operatorKeyword; /// Gets the operator token. @@ -24371,12 +24388,13 @@ internal OperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, G 0 => this.attributeLists, 1 => this.modifiers, 2 => this.returnType, - 3 => this.operatorKeyword, - 4 => this.operatorToken, - 5 => this.parameterList, - 6 => this.body, - 7 => this.expressionBody, - 8 => this.semicolonToken, + 3 => this.explicitInterfaceSpecifier, + 4 => this.operatorKeyword, + 5 => this.operatorToken, + 6 => this.parameterList, + 7 => this.body, + 8 => this.expressionBody, + 9 => this.semicolonToken, _ => null, }; @@ -24385,11 +24403,11 @@ internal OperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, G public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitOperatorDeclaration(this); public override TResult Accept(CSharpSyntaxVisitor visitor) => visitor.VisitOperatorDeclaration(this); - public OperatorDeclarationSyntax Update(Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList attributeLists, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList modifiers, TypeSyntax returnType, SyntaxToken operatorKeyword, SyntaxToken operatorToken, ParameterListSyntax parameterList, BlockSyntax body, ArrowExpressionClauseSyntax expressionBody, SyntaxToken semicolonToken) + public OperatorDeclarationSyntax Update(Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList attributeLists, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList modifiers, TypeSyntax returnType, ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier, SyntaxToken operatorKeyword, SyntaxToken operatorToken, ParameterListSyntax parameterList, BlockSyntax body, ArrowExpressionClauseSyntax expressionBody, SyntaxToken semicolonToken) { - if (attributeLists != this.AttributeLists || modifiers != this.Modifiers || returnType != this.ReturnType || operatorKeyword != this.OperatorKeyword || operatorToken != this.OperatorToken || parameterList != this.ParameterList || body != this.Body || expressionBody != this.ExpressionBody || semicolonToken != this.SemicolonToken) + if (attributeLists != this.AttributeLists || modifiers != this.Modifiers || returnType != this.ReturnType || explicitInterfaceSpecifier != this.ExplicitInterfaceSpecifier || operatorKeyword != this.OperatorKeyword || operatorToken != this.OperatorToken || parameterList != this.ParameterList || body != this.Body || expressionBody != this.ExpressionBody || semicolonToken != this.SemicolonToken) { - var newNode = SyntaxFactory.OperatorDeclaration(attributeLists, modifiers, returnType, operatorKeyword, operatorToken, parameterList, body, expressionBody, semicolonToken); + var newNode = SyntaxFactory.OperatorDeclaration(attributeLists, modifiers, returnType, explicitInterfaceSpecifier, operatorKeyword, operatorToken, parameterList, body, expressionBody, semicolonToken); var diags = GetDiagnostics(); if (diags?.Length > 0) newNode = newNode.WithDiagnosticsGreen(diags); @@ -24403,15 +24421,15 @@ public OperatorDeclarationSyntax Update(Microsoft.CodeAnalysis.Syntax.InternalSy } internal override GreenNode SetDiagnostics(DiagnosticInfo[]? diagnostics) - => new OperatorDeclarationSyntax(this.Kind, this.attributeLists, this.modifiers, this.returnType, this.operatorKeyword, this.operatorToken, this.parameterList, this.body, this.expressionBody, this.semicolonToken, diagnostics, GetAnnotations()); + => new OperatorDeclarationSyntax(this.Kind, this.attributeLists, this.modifiers, this.returnType, this.explicitInterfaceSpecifier, this.operatorKeyword, this.operatorToken, this.parameterList, this.body, this.expressionBody, this.semicolonToken, diagnostics, GetAnnotations()); internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations) - => new OperatorDeclarationSyntax(this.Kind, this.attributeLists, this.modifiers, this.returnType, this.operatorKeyword, this.operatorToken, this.parameterList, this.body, this.expressionBody, this.semicolonToken, GetDiagnostics(), annotations); + => new OperatorDeclarationSyntax(this.Kind, this.attributeLists, this.modifiers, this.returnType, this.explicitInterfaceSpecifier, this.operatorKeyword, this.operatorToken, this.parameterList, this.body, this.expressionBody, this.semicolonToken, GetDiagnostics(), annotations); internal OperatorDeclarationSyntax(ObjectReader reader) : base(reader) { - this.SlotCount = 9; + this.SlotCount = 10; var attributeLists = (GreenNode?)reader.ReadValue(); if (attributeLists != null) { @@ -24427,6 +24445,12 @@ internal OperatorDeclarationSyntax(ObjectReader reader) var returnType = (TypeSyntax)reader.ReadValue(); AdjustFlagsAndWidth(returnType); this.returnType = returnType; + var explicitInterfaceSpecifier = (ExplicitInterfaceSpecifierSyntax?)reader.ReadValue(); + if (explicitInterfaceSpecifier != null) + { + AdjustFlagsAndWidth(explicitInterfaceSpecifier); + this.explicitInterfaceSpecifier = explicitInterfaceSpecifier; + } var operatorKeyword = (SyntaxToken)reader.ReadValue(); AdjustFlagsAndWidth(operatorKeyword); this.operatorKeyword = operatorKeyword; @@ -24462,6 +24486,7 @@ internal override void WriteTo(ObjectWriter writer) writer.WriteValue(this.attributeLists); writer.WriteValue(this.modifiers); writer.WriteValue(this.returnType); + writer.WriteValue(this.explicitInterfaceSpecifier); writer.WriteValue(this.operatorKeyword); writer.WriteValue(this.operatorToken); writer.WriteValue(this.parameterList); @@ -33946,7 +33971,7 @@ public override CSharpSyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), (TypeSyntax)Visit(node.ReturnType), (ExplicitInterfaceSpecifierSyntax)Visit(node.ExplicitInterfaceSpecifier), (SyntaxToken)Visit(node.Identifier), (TypeParameterListSyntax)Visit(node.TypeParameterList), (ParameterListSyntax)Visit(node.ParameterList), VisitList(node.ConstraintClauses), (BlockSyntax)Visit(node.Body), (ArrowExpressionClauseSyntax)Visit(node.ExpressionBody), (SyntaxToken)Visit(node.SemicolonToken)); public override CSharpSyntaxNode VisitOperatorDeclaration(OperatorDeclarationSyntax node) - => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), (TypeSyntax)Visit(node.ReturnType), (SyntaxToken)Visit(node.OperatorKeyword), (SyntaxToken)Visit(node.OperatorToken), (ParameterListSyntax)Visit(node.ParameterList), (BlockSyntax)Visit(node.Body), (ArrowExpressionClauseSyntax)Visit(node.ExpressionBody), (SyntaxToken)Visit(node.SemicolonToken)); + => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), (TypeSyntax)Visit(node.ReturnType), (ExplicitInterfaceSpecifierSyntax)Visit(node.ExplicitInterfaceSpecifier), (SyntaxToken)Visit(node.OperatorKeyword), (SyntaxToken)Visit(node.OperatorToken), (ParameterListSyntax)Visit(node.ParameterList), (BlockSyntax)Visit(node.Body), (ArrowExpressionClauseSyntax)Visit(node.ExpressionBody), (SyntaxToken)Visit(node.SemicolonToken)); public override CSharpSyntaxNode VisitConversionOperatorDeclaration(ConversionOperatorDeclarationSyntax node) => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), (SyntaxToken)Visit(node.ImplicitOrExplicitKeyword), (SyntaxToken)Visit(node.OperatorKeyword), (TypeSyntax)Visit(node.Type), (ParameterListSyntax)Visit(node.ParameterList), (BlockSyntax)Visit(node.Body), (ArrowExpressionClauseSyntax)Visit(node.ExpressionBody), (SyntaxToken)Visit(node.SemicolonToken)); @@ -37791,7 +37816,7 @@ public MethodDeclarationSyntax MethodDeclaration(Microsoft.CodeAnalysis.Syntax.I return new MethodDeclarationSyntax(SyntaxKind.MethodDeclaration, attributeLists.Node, modifiers.Node, returnType, explicitInterfaceSpecifier, identifier, typeParameterList, parameterList, constraintClauses.Node, body, expressionBody, semicolonToken, this.context); } - public OperatorDeclarationSyntax OperatorDeclaration(Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList attributeLists, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList modifiers, TypeSyntax returnType, SyntaxToken operatorKeyword, SyntaxToken operatorToken, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken) + public OperatorDeclarationSyntax OperatorDeclaration(Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList attributeLists, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList modifiers, TypeSyntax returnType, ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, SyntaxToken operatorKeyword, SyntaxToken operatorToken, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken) { #if DEBUG if (returnType == null) throw new ArgumentNullException(nameof(returnType)); @@ -37837,7 +37862,7 @@ public OperatorDeclarationSyntax OperatorDeclaration(Microsoft.CodeAnalysis.Synt } #endif - return new OperatorDeclarationSyntax(SyntaxKind.OperatorDeclaration, attributeLists.Node, modifiers.Node, returnType, operatorKeyword, operatorToken, parameterList, body, expressionBody, semicolonToken, this.context); + return new OperatorDeclarationSyntax(SyntaxKind.OperatorDeclaration, attributeLists.Node, modifiers.Node, returnType, explicitInterfaceSpecifier, operatorKeyword, operatorToken, parameterList, body, expressionBody, semicolonToken, this.context); } public ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration(Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList attributeLists, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList modifiers, SyntaxToken implicitOrExplicitKeyword, SyntaxToken operatorKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken) @@ -42660,7 +42685,7 @@ public static MethodDeclarationSyntax MethodDeclaration(Microsoft.CodeAnalysis.S return new MethodDeclarationSyntax(SyntaxKind.MethodDeclaration, attributeLists.Node, modifiers.Node, returnType, explicitInterfaceSpecifier, identifier, typeParameterList, parameterList, constraintClauses.Node, body, expressionBody, semicolonToken); } - public static OperatorDeclarationSyntax OperatorDeclaration(Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList attributeLists, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList modifiers, TypeSyntax returnType, SyntaxToken operatorKeyword, SyntaxToken operatorToken, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken) + public static OperatorDeclarationSyntax OperatorDeclaration(Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList attributeLists, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList modifiers, TypeSyntax returnType, ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, SyntaxToken operatorKeyword, SyntaxToken operatorToken, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken) { #if DEBUG if (returnType == null) throw new ArgumentNullException(nameof(returnType)); @@ -42706,7 +42731,7 @@ public static OperatorDeclarationSyntax OperatorDeclaration(Microsoft.CodeAnalys } #endif - return new OperatorDeclarationSyntax(SyntaxKind.OperatorDeclaration, attributeLists.Node, modifiers.Node, returnType, operatorKeyword, operatorToken, parameterList, body, expressionBody, semicolonToken); + return new OperatorDeclarationSyntax(SyntaxKind.OperatorDeclaration, attributeLists.Node, modifiers.Node, returnType, explicitInterfaceSpecifier, operatorKeyword, operatorToken, parameterList, body, expressionBody, semicolonToken); } public static ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration(Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList attributeLists, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList modifiers, SyntaxToken implicitOrExplicitKeyword, SyntaxToken operatorKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs index 86a0e9c3dc084..cd29148ea963d 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs @@ -1927,7 +1927,7 @@ public partial class CSharpSyntaxRewriter : CSharpSyntaxVisitor => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), (TypeSyntax?)Visit(node.ReturnType) ?? throw new ArgumentNullException("returnType"), (ExplicitInterfaceSpecifierSyntax?)Visit(node.ExplicitInterfaceSpecifier), VisitToken(node.Identifier), (TypeParameterListSyntax?)Visit(node.TypeParameterList), (ParameterListSyntax?)Visit(node.ParameterList) ?? throw new ArgumentNullException("parameterList"), VisitList(node.ConstraintClauses), (BlockSyntax?)Visit(node.Body), (ArrowExpressionClauseSyntax?)Visit(node.ExpressionBody), VisitToken(node.SemicolonToken)); public override SyntaxNode? VisitOperatorDeclaration(OperatorDeclarationSyntax node) - => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), (TypeSyntax?)Visit(node.ReturnType) ?? throw new ArgumentNullException("returnType"), VisitToken(node.OperatorKeyword), VisitToken(node.OperatorToken), (ParameterListSyntax?)Visit(node.ParameterList) ?? throw new ArgumentNullException("parameterList"), (BlockSyntax?)Visit(node.Body), (ArrowExpressionClauseSyntax?)Visit(node.ExpressionBody), VisitToken(node.SemicolonToken)); + => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), (TypeSyntax?)Visit(node.ReturnType) ?? throw new ArgumentNullException("returnType"), (ExplicitInterfaceSpecifierSyntax?)Visit(node.ExplicitInterfaceSpecifier), VisitToken(node.OperatorKeyword), VisitToken(node.OperatorToken), (ParameterListSyntax?)Visit(node.ParameterList) ?? throw new ArgumentNullException("parameterList"), (BlockSyntax?)Visit(node.Body), (ArrowExpressionClauseSyntax?)Visit(node.ExpressionBody), VisitToken(node.SemicolonToken)); public override SyntaxNode? VisitConversionOperatorDeclaration(ConversionOperatorDeclarationSyntax node) => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), VisitToken(node.ImplicitOrExplicitKeyword), VisitToken(node.OperatorKeyword), (TypeSyntax?)Visit(node.Type) ?? throw new ArgumentNullException("type"), (ParameterListSyntax?)Visit(node.ParameterList) ?? throw new ArgumentNullException("parameterList"), (BlockSyntax?)Visit(node.Body), (ArrowExpressionClauseSyntax?)Visit(node.ExpressionBody), VisitToken(node.SemicolonToken)); @@ -5091,7 +5091,7 @@ public static MethodDeclarationSyntax MethodDeclaration(TypeSyntax returnType, s => SyntaxFactory.MethodDeclaration(default, default(SyntaxTokenList), returnType, default, SyntaxFactory.Identifier(identifier), default, SyntaxFactory.ParameterList(), default, default, default, default); /// Creates a new OperatorDeclarationSyntax instance. - public static OperatorDeclarationSyntax OperatorDeclaration(SyntaxList attributeLists, SyntaxTokenList modifiers, TypeSyntax returnType, SyntaxToken operatorKeyword, SyntaxToken operatorToken, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken semicolonToken) + public static OperatorDeclarationSyntax OperatorDeclaration(SyntaxList attributeLists, SyntaxTokenList modifiers, TypeSyntax returnType, ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, SyntaxToken operatorKeyword, SyntaxToken operatorToken, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken semicolonToken) { if (returnType == null) throw new ArgumentNullException(nameof(returnType)); if (operatorKeyword.Kind() != SyntaxKind.OperatorKeyword) throw new ArgumentException(nameof(operatorKeyword)); @@ -5129,16 +5129,16 @@ public static OperatorDeclarationSyntax OperatorDeclaration(SyntaxList(), modifiers.Node.ToGreenList(), (Syntax.InternalSyntax.TypeSyntax)returnType.Green, (Syntax.InternalSyntax.SyntaxToken)operatorKeyword.Node!, (Syntax.InternalSyntax.SyntaxToken)operatorToken.Node!, (Syntax.InternalSyntax.ParameterListSyntax)parameterList.Green, body == null ? null : (Syntax.InternalSyntax.BlockSyntax)body.Green, expressionBody == null ? null : (Syntax.InternalSyntax.ArrowExpressionClauseSyntax)expressionBody.Green, (Syntax.InternalSyntax.SyntaxToken?)semicolonToken.Node).CreateRed(); + return (OperatorDeclarationSyntax)Syntax.InternalSyntax.SyntaxFactory.OperatorDeclaration(attributeLists.Node.ToGreenList(), modifiers.Node.ToGreenList(), (Syntax.InternalSyntax.TypeSyntax)returnType.Green, explicitInterfaceSpecifier == null ? null : (Syntax.InternalSyntax.ExplicitInterfaceSpecifierSyntax)explicitInterfaceSpecifier.Green, (Syntax.InternalSyntax.SyntaxToken)operatorKeyword.Node!, (Syntax.InternalSyntax.SyntaxToken)operatorToken.Node!, (Syntax.InternalSyntax.ParameterListSyntax)parameterList.Green, body == null ? null : (Syntax.InternalSyntax.BlockSyntax)body.Green, expressionBody == null ? null : (Syntax.InternalSyntax.ArrowExpressionClauseSyntax)expressionBody.Green, (Syntax.InternalSyntax.SyntaxToken?)semicolonToken.Node).CreateRed(); } /// Creates a new OperatorDeclarationSyntax instance. - public static OperatorDeclarationSyntax OperatorDeclaration(SyntaxList attributeLists, SyntaxTokenList modifiers, TypeSyntax returnType, SyntaxToken operatorToken, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody) - => SyntaxFactory.OperatorDeclaration(attributeLists, modifiers, returnType, SyntaxFactory.Token(SyntaxKind.OperatorKeyword), operatorToken, parameterList, body, expressionBody, default); + public static OperatorDeclarationSyntax OperatorDeclaration(SyntaxList attributeLists, SyntaxTokenList modifiers, TypeSyntax returnType, ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, SyntaxToken operatorToken, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody) + => SyntaxFactory.OperatorDeclaration(attributeLists, modifiers, returnType, explicitInterfaceSpecifier, SyntaxFactory.Token(SyntaxKind.OperatorKeyword), operatorToken, parameterList, body, expressionBody, default); /// Creates a new OperatorDeclarationSyntax instance. public static OperatorDeclarationSyntax OperatorDeclaration(TypeSyntax returnType, SyntaxToken operatorToken) - => SyntaxFactory.OperatorDeclaration(default, default(SyntaxTokenList), returnType, SyntaxFactory.Token(SyntaxKind.OperatorKeyword), operatorToken, SyntaxFactory.ParameterList(), default, default, default); + => SyntaxFactory.OperatorDeclaration(default, default(SyntaxTokenList), returnType, default, SyntaxFactory.Token(SyntaxKind.OperatorKeyword), operatorToken, SyntaxFactory.ParameterList(), default, default, default); /// Creates a new ConversionOperatorDeclarationSyntax instance. public static ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration(SyntaxList attributeLists, SyntaxTokenList modifiers, SyntaxToken implicitOrExplicitKeyword, SyntaxToken operatorKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken semicolonToken) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs index 04aa76e5e9761..11f6cba9fd095 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs @@ -11443,6 +11443,7 @@ public sealed partial class OperatorDeclarationSyntax : BaseMethodDeclarationSyn { private SyntaxNode? attributeLists; private TypeSyntax? returnType; + private ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier; private ParameterListSyntax? parameterList; private BlockSyntax? body; private ArrowExpressionClauseSyntax? expressionBody; @@ -11466,17 +11467,19 @@ public override SyntaxTokenList Modifiers /// Gets the return type. public TypeSyntax ReturnType => GetRed(ref this.returnType, 2)!; + public ExplicitInterfaceSpecifierSyntax? ExplicitInterfaceSpecifier => GetRed(ref this.explicitInterfaceSpecifier, 3); + /// Gets the "operator" keyword. - public SyntaxToken OperatorKeyword => new SyntaxToken(this, ((Syntax.InternalSyntax.OperatorDeclarationSyntax)this.Green).operatorKeyword, GetChildPosition(3), GetChildIndex(3)); + public SyntaxToken OperatorKeyword => new SyntaxToken(this, ((Syntax.InternalSyntax.OperatorDeclarationSyntax)this.Green).operatorKeyword, GetChildPosition(4), GetChildIndex(4)); /// Gets the operator token. - public SyntaxToken OperatorToken => new SyntaxToken(this, ((Syntax.InternalSyntax.OperatorDeclarationSyntax)this.Green).operatorToken, GetChildPosition(4), GetChildIndex(4)); + public SyntaxToken OperatorToken => new SyntaxToken(this, ((Syntax.InternalSyntax.OperatorDeclarationSyntax)this.Green).operatorToken, GetChildPosition(5), GetChildIndex(5)); - public override ParameterListSyntax ParameterList => GetRed(ref this.parameterList, 5)!; + public override ParameterListSyntax ParameterList => GetRed(ref this.parameterList, 6)!; - public override BlockSyntax? Body => GetRed(ref this.body, 6); + public override BlockSyntax? Body => GetRed(ref this.body, 7); - public override ArrowExpressionClauseSyntax? ExpressionBody => GetRed(ref this.expressionBody, 7); + public override ArrowExpressionClauseSyntax? ExpressionBody => GetRed(ref this.expressionBody, 8); /// Gets the optional semicolon token. public override SyntaxToken SemicolonToken @@ -11484,7 +11487,7 @@ public override SyntaxToken SemicolonToken get { var slot = ((Syntax.InternalSyntax.OperatorDeclarationSyntax)this.Green).semicolonToken; - return slot != null ? new SyntaxToken(this, slot, GetChildPosition(8), GetChildIndex(8)) : default; + return slot != null ? new SyntaxToken(this, slot, GetChildPosition(9), GetChildIndex(9)) : default; } } @@ -11493,9 +11496,10 @@ public override SyntaxToken SemicolonToken { 0 => GetRedAtZero(ref this.attributeLists)!, 2 => GetRed(ref this.returnType, 2)!, - 5 => GetRed(ref this.parameterList, 5)!, - 6 => GetRed(ref this.body, 6), - 7 => GetRed(ref this.expressionBody, 7), + 3 => GetRed(ref this.explicitInterfaceSpecifier, 3), + 6 => GetRed(ref this.parameterList, 6)!, + 7 => GetRed(ref this.body, 7), + 8 => GetRed(ref this.expressionBody, 8), _ => null, }; @@ -11504,20 +11508,21 @@ public override SyntaxToken SemicolonToken { 0 => this.attributeLists, 2 => this.returnType, - 5 => this.parameterList, - 6 => this.body, - 7 => this.expressionBody, + 3 => this.explicitInterfaceSpecifier, + 6 => this.parameterList, + 7 => this.body, + 8 => this.expressionBody, _ => null, }; public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitOperatorDeclaration(this); public override TResult? Accept(CSharpSyntaxVisitor visitor) where TResult : default => visitor.VisitOperatorDeclaration(this); - public OperatorDeclarationSyntax Update(SyntaxList attributeLists, SyntaxTokenList modifiers, TypeSyntax returnType, SyntaxToken operatorKeyword, SyntaxToken operatorToken, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken semicolonToken) + public OperatorDeclarationSyntax Update(SyntaxList attributeLists, SyntaxTokenList modifiers, TypeSyntax returnType, ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, SyntaxToken operatorKeyword, SyntaxToken operatorToken, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken semicolonToken) { - if (attributeLists != this.AttributeLists || modifiers != this.Modifiers || returnType != this.ReturnType || operatorKeyword != this.OperatorKeyword || operatorToken != this.OperatorToken || parameterList != this.ParameterList || body != this.Body || expressionBody != this.ExpressionBody || semicolonToken != this.SemicolonToken) + if (attributeLists != this.AttributeLists || modifiers != this.Modifiers || returnType != this.ReturnType || explicitInterfaceSpecifier != this.ExplicitInterfaceSpecifier || operatorKeyword != this.OperatorKeyword || operatorToken != this.OperatorToken || parameterList != this.ParameterList || body != this.Body || expressionBody != this.ExpressionBody || semicolonToken != this.SemicolonToken) { - var newNode = SyntaxFactory.OperatorDeclaration(attributeLists, modifiers, returnType, operatorKeyword, operatorToken, parameterList, body, expressionBody, semicolonToken); + var newNode = SyntaxFactory.OperatorDeclaration(attributeLists, modifiers, returnType, explicitInterfaceSpecifier, operatorKeyword, operatorToken, parameterList, body, expressionBody, semicolonToken); var annotations = GetAnnotations(); return annotations?.Length > 0 ? newNode.WithAnnotations(annotations) : newNode; } @@ -11526,20 +11531,21 @@ public OperatorDeclarationSyntax Update(SyntaxList attribut } internal override MemberDeclarationSyntax WithAttributeListsCore(SyntaxList attributeLists) => WithAttributeLists(attributeLists); - public new OperatorDeclarationSyntax WithAttributeLists(SyntaxList attributeLists) => Update(attributeLists, this.Modifiers, this.ReturnType, this.OperatorKeyword, this.OperatorToken, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); + public new OperatorDeclarationSyntax WithAttributeLists(SyntaxList attributeLists) => Update(attributeLists, this.Modifiers, this.ReturnType, this.ExplicitInterfaceSpecifier, this.OperatorKeyword, this.OperatorToken, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); internal override MemberDeclarationSyntax WithModifiersCore(SyntaxTokenList modifiers) => WithModifiers(modifiers); - public new OperatorDeclarationSyntax WithModifiers(SyntaxTokenList modifiers) => Update(this.AttributeLists, modifiers, this.ReturnType, this.OperatorKeyword, this.OperatorToken, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); - public OperatorDeclarationSyntax WithReturnType(TypeSyntax returnType) => Update(this.AttributeLists, this.Modifiers, returnType, this.OperatorKeyword, this.OperatorToken, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); - public OperatorDeclarationSyntax WithOperatorKeyword(SyntaxToken operatorKeyword) => Update(this.AttributeLists, this.Modifiers, this.ReturnType, operatorKeyword, this.OperatorToken, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); - public OperatorDeclarationSyntax WithOperatorToken(SyntaxToken operatorToken) => Update(this.AttributeLists, this.Modifiers, this.ReturnType, this.OperatorKeyword, operatorToken, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); + public new OperatorDeclarationSyntax WithModifiers(SyntaxTokenList modifiers) => Update(this.AttributeLists, modifiers, this.ReturnType, this.ExplicitInterfaceSpecifier, this.OperatorKeyword, this.OperatorToken, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); + public OperatorDeclarationSyntax WithReturnType(TypeSyntax returnType) => Update(this.AttributeLists, this.Modifiers, returnType, this.ExplicitInterfaceSpecifier, this.OperatorKeyword, this.OperatorToken, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); + public OperatorDeclarationSyntax WithExplicitInterfaceSpecifier(ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier) => Update(this.AttributeLists, this.Modifiers, this.ReturnType, explicitInterfaceSpecifier, this.OperatorKeyword, this.OperatorToken, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); + public OperatorDeclarationSyntax WithOperatorKeyword(SyntaxToken operatorKeyword) => Update(this.AttributeLists, this.Modifiers, this.ReturnType, this.ExplicitInterfaceSpecifier, operatorKeyword, this.OperatorToken, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); + public OperatorDeclarationSyntax WithOperatorToken(SyntaxToken operatorToken) => Update(this.AttributeLists, this.Modifiers, this.ReturnType, this.ExplicitInterfaceSpecifier, this.OperatorKeyword, operatorToken, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); internal override BaseMethodDeclarationSyntax WithParameterListCore(ParameterListSyntax parameterList) => WithParameterList(parameterList); - public new OperatorDeclarationSyntax WithParameterList(ParameterListSyntax parameterList) => Update(this.AttributeLists, this.Modifiers, this.ReturnType, this.OperatorKeyword, this.OperatorToken, parameterList, this.Body, this.ExpressionBody, this.SemicolonToken); + public new OperatorDeclarationSyntax WithParameterList(ParameterListSyntax parameterList) => Update(this.AttributeLists, this.Modifiers, this.ReturnType, this.ExplicitInterfaceSpecifier, this.OperatorKeyword, this.OperatorToken, parameterList, this.Body, this.ExpressionBody, this.SemicolonToken); internal override BaseMethodDeclarationSyntax WithBodyCore(BlockSyntax? body) => WithBody(body); - public new OperatorDeclarationSyntax WithBody(BlockSyntax? body) => Update(this.AttributeLists, this.Modifiers, this.ReturnType, this.OperatorKeyword, this.OperatorToken, this.ParameterList, body, this.ExpressionBody, this.SemicolonToken); + public new OperatorDeclarationSyntax WithBody(BlockSyntax? body) => Update(this.AttributeLists, this.Modifiers, this.ReturnType, this.ExplicitInterfaceSpecifier, this.OperatorKeyword, this.OperatorToken, this.ParameterList, body, this.ExpressionBody, this.SemicolonToken); internal override BaseMethodDeclarationSyntax WithExpressionBodyCore(ArrowExpressionClauseSyntax? expressionBody) => WithExpressionBody(expressionBody); - public new OperatorDeclarationSyntax WithExpressionBody(ArrowExpressionClauseSyntax? expressionBody) => Update(this.AttributeLists, this.Modifiers, this.ReturnType, this.OperatorKeyword, this.OperatorToken, this.ParameterList, this.Body, expressionBody, this.SemicolonToken); + public new OperatorDeclarationSyntax WithExpressionBody(ArrowExpressionClauseSyntax? expressionBody) => Update(this.AttributeLists, this.Modifiers, this.ReturnType, this.ExplicitInterfaceSpecifier, this.OperatorKeyword, this.OperatorToken, this.ParameterList, this.Body, expressionBody, this.SemicolonToken); internal override BaseMethodDeclarationSyntax WithSemicolonTokenCore(SyntaxToken semicolonToken) => WithSemicolonToken(semicolonToken); - public new OperatorDeclarationSyntax WithSemicolonToken(SyntaxToken semicolonToken) => Update(this.AttributeLists, this.Modifiers, this.ReturnType, this.OperatorKeyword, this.OperatorToken, this.ParameterList, this.Body, this.ExpressionBody, semicolonToken); + public new OperatorDeclarationSyntax WithSemicolonToken(SyntaxToken semicolonToken) => Update(this.AttributeLists, this.Modifiers, this.ReturnType, this.ExplicitInterfaceSpecifier, this.OperatorKeyword, this.OperatorToken, this.ParameterList, this.Body, this.ExpressionBody, semicolonToken); internal override MemberDeclarationSyntax AddAttributeListsCore(params AttributeListSyntax[] items) => AddAttributeLists(items); public new OperatorDeclarationSyntax AddAttributeLists(params AttributeListSyntax[] items) => WithAttributeLists(this.AttributeLists.AddRange(items)); diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 24b4d78971d71..0c10b0204a253 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -2286,7 +2286,7 @@ private MemberDeclarationSyntax ParseMemberDeclarationOrStatementCore(SyntaxKind try { - if ((!haveAttributes || !IsScript) && !haveModifiers && (type.Kind == SyntaxKind.RefType || !IsOperatorKeyword())) + if ((!haveAttributes || !IsScript) && !haveModifiers && (type.Kind == SyntaxKind.RefType || !IsOperatorStart(out _, advanceParser: false))) { this.Reset(ref afterAttributesPoint); @@ -2332,12 +2332,13 @@ private MemberDeclarationSyntax ParseMemberDeclarationOrStatementCore(SyntaxKind parse_member_name:; // If we've seen the ref keyword, we know we must have an indexer, method, property, or local. bool typeIsRef = type.IsRef; + ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt; // Check here for operators // Allow old-style implicit/explicit casting operator syntax, just so we can give a better error - if (!typeIsRef && IsOperatorKeyword()) + if (!typeIsRef && IsOperatorStart(out explicitInterfaceOpt)) { - return this.ParseOperatorDeclaration(attributes, modifiers, type); + return this.ParseOperatorDeclaration(attributes, modifiers, type, explicitInterfaceOpt); } if ((!typeIsRef || !IsScript) && IsFieldDeclaration(isEvent: false)) @@ -2374,7 +2375,6 @@ private MemberDeclarationSyntax ParseMemberDeclarationOrStatementCore(SyntaxKind // At this point we can either have indexers, methods, or // properties (or something unknown). Try to break apart // the following name and determine what to do from there. - ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt; SyntaxToken identifierOrThisOpt; TypeParameterListSyntax typeParameterListOpt; this.ParseMemberName(out explicitInterfaceOpt, out identifierOrThisOpt, out typeParameterListOpt, isEvent: false); @@ -2716,14 +2716,16 @@ private MemberDeclarationSyntax ParseMemberDeclarationCore(SyntaxKind parentKind } parse_member_name:; + ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt; + // If we've seen the ref keyword, we know we must have an indexer, method, or property. if (type.Kind != SyntaxKind.RefType) { // Check here for operators // Allow old-style implicit/explicit casting operator syntax, just so we can give a better error - if (IsOperatorKeyword()) + if (IsOperatorStart(out explicitInterfaceOpt)) { - return this.ParseOperatorDeclaration(attributes, modifiers, type); + return this.ParseOperatorDeclaration(attributes, modifiers, type, explicitInterfaceOpt); } if (IsFieldDeclaration(isEvent: false)) @@ -2735,7 +2737,6 @@ private MemberDeclarationSyntax ParseMemberDeclarationCore(SyntaxKind parentKind // At this point we can either have indexers, methods, or // properties (or something unknown). Try to break apart // the following name and determine what to do from there. - ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt; SyntaxToken identifierOrThisOpt; TypeParameterListSyntax typeParameterListOpt; this.ParseMemberName(out explicitInterfaceOpt, out identifierOrThisOpt, out typeParameterListOpt, isEvent: false); @@ -3193,7 +3194,8 @@ private ConversionOperatorDeclarationSyntax ParseConversionOperatorDeclaration(S private OperatorDeclarationSyntax ParseOperatorDeclaration( SyntaxList attributes, SyntaxListBuilder modifiers, - TypeSyntax type) + TypeSyntax type, + ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt) { var opKeyword = this.EatToken(SyntaxKind.OperatorKeyword); SyntaxToken opToken; @@ -3313,6 +3315,7 @@ private OperatorDeclarationSyntax ParseOperatorDeclaration( attributes, modifiers.ToList(), type, + explicitInterfaceOpt, opKeyword, opToken, paramList, @@ -5935,37 +5938,7 @@ private void ParseMemberName( else { // If we saw a . or :: then we must have something explicit. - // first parse the upcoming name portion. - - var saveTerm = _termState; - _termState |= TerminatorState.IsEndOfNameInExplicitInterface; - - if (explicitInterfaceName == null) - { - // If this is the first time, then just get the next simple - // name and store it as the explicit interface name. - explicitInterfaceName = this.ParseSimpleName(NameOptions.InTypeList); - - // Now, get the next separator. - separator = this.CurrentToken.Kind == SyntaxKind.ColonColonToken - ? this.EatToken() // fine after the first identifier - : this.EatToken(SyntaxKind.DotToken); - } - else - { - // Parse out the next part and combine it with the - // current explicit name to form the new explicit name. - var tmp = this.ParseQualifiedNameRight(NameOptions.InTypeList, explicitInterfaceName, separator); - Debug.Assert(!ReferenceEquals(tmp, explicitInterfaceName), "We should have consumed something and updated explicitInterfaceName"); - explicitInterfaceName = tmp; - - // Now, get the next separator. - separator = this.CurrentToken.Kind == SyntaxKind.ColonColonToken - ? this.ConvertToMissingWithTrailingTrivia(this.EatToken(), SyntaxKind.DotToken) - : this.EatToken(SyntaxKind.DotToken); - } - - _termState = saveTerm; + AccumulateExplicitInterfaceName(ref explicitInterfaceName, ref separator); } } @@ -6034,6 +6007,144 @@ private void ParseMemberName( } } + private void AccumulateExplicitInterfaceName(ref NameSyntax explicitInterfaceName, ref SyntaxToken separator, bool reportAnErrorOnMispacedColonColon = false) + { + // first parse the upcoming name portion. + + var saveTerm = _termState; + _termState |= TerminatorState.IsEndOfNameInExplicitInterface; + + if (explicitInterfaceName == null) + { + // If this is the first time, then just get the next simple + // name and store it as the explicit interface name. + explicitInterfaceName = this.ParseSimpleName(NameOptions.InTypeList); + + // Now, get the next separator. + separator = this.CurrentToken.Kind == SyntaxKind.ColonColonToken + ? this.EatToken() // fine after the first identifier + : this.EatToken(SyntaxKind.DotToken); + } + else + { + // Parse out the next part and combine it with the + // current explicit name to form the new explicit name. + var tmp = this.ParseQualifiedNameRight(NameOptions.InTypeList, explicitInterfaceName, separator); + Debug.Assert(!ReferenceEquals(tmp, explicitInterfaceName), "We should have consumed something and updated explicitInterfaceName"); + explicitInterfaceName = tmp; + + // Now, get the next separator. + if (this.CurrentToken.Kind == SyntaxKind.ColonColonToken) + { + separator = this.EatToken(); + + if (reportAnErrorOnMispacedColonColon) + { + // The https://github.com/dotnet/roslyn/issues/53021 is tracking fixing this in general + separator = this.AddError(separator, ErrorCode.ERR_UnexpectedAliasedName); + } + + separator = this.ConvertToMissingWithTrailingTrivia(separator, SyntaxKind.DotToken); + } + else + { + separator = this.EatToken(SyntaxKind.DotToken); + } + } + + _termState = saveTerm; + } + + /// + /// This is an adjusted version of . + /// When it returns true, it stops at operator keyword (). + /// When it returns false, it does not advance in the token stream. + /// + private bool IsOperatorStart(out ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt, bool advanceParser = true) + { + explicitInterfaceOpt = null; + + if (IsOperatorKeyword()) + { + return true; + } + + if (this.CurrentToken.Kind != SyntaxKind.IdentifierToken) + { + return false; + } + + NameSyntax explicitInterfaceName = null; + SyntaxToken separator = null; + + ResetPoint beforeIdentifierPoint = GetResetPoint(); + + try + { + while (true) + { + // now, scan past the next name. if it's followed by a dot then + // it's part of the explicit name we're building up. Otherwise, + // it should be an operator token + var point = GetResetPoint(); + bool isPartOfInterfaceName; + try + { + ScanNamedTypePart(); + isPartOfInterfaceName = IsDotOrColonColon(); + } + finally + { + this.Reset(ref point); + this.Release(ref point); + } + + if (!isPartOfInterfaceName) + { + // We're past any explicit interface portion + if (separator != null && separator.Kind == SyntaxKind.ColonColonToken) + { + separator = this.AddError(separator, ErrorCode.ERR_AliasQualAsExpression); + separator = this.ConvertToMissingWithTrailingTrivia(separator, SyntaxKind.DotToken); + } + + break; + } + else + { + // If we saw a . or :: then we must have something explicit. + AccumulateExplicitInterfaceName(ref explicitInterfaceName, ref separator, reportAnErrorOnMispacedColonColon: true); + } + } + + if (!IsOperatorKeyword() || explicitInterfaceName is null) + { + Reset(ref beforeIdentifierPoint); + return false; + } + + if (!advanceParser) + { + Reset(ref beforeIdentifierPoint); + return true; + } + + if (separator.Kind != SyntaxKind.DotToken) + { + separator = WithAdditionalDiagnostics(separator, GetExpectedTokenError(SyntaxKind.DotToken, separator.Kind, separator.GetLeadingTriviaWidth(), separator.Width)); + separator = ConvertToMissingWithTrailingTrivia(separator, SyntaxKind.DotToken); + } + + explicitInterfaceOpt = CheckFeatureAvailability(_syntaxFactory.ExplicitInterfaceSpecifier(explicitInterfaceName, separator), MessageID.IDS_FeatureStaticAbstractMembersInInterfaces); + + return true; + } + finally + { + Release(ref beforeIdentifierPoint); + } + } + private NameSyntax ParseAliasQualifiedName(NameOptions allowedParts = NameOptions.None) { NameSyntax name = this.ParseSimpleName(allowedParts); diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index 47fdde3be2e2f..b38f40a9f9ea9 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -1 +1,6 @@ override Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetUsedAssemblyReferences(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Immutable.ImmutableArray +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.OperatorDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.CSharp.Syntax.ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier, Microsoft.CodeAnalysis.SyntaxToken operatorKeyword, Microsoft.CodeAnalysis.SyntaxToken operatorToken, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.OperatorDeclarationSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.OperatorDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.CSharp.Syntax.ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier, Microsoft.CodeAnalysis.SyntaxToken operatorToken, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.OperatorDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.OperatorDeclarationSyntax.ExplicitInterfaceSpecifier.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ExplicitInterfaceSpecifierSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.OperatorDeclarationSyntax.Update(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.CSharp.Syntax.ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier, Microsoft.CodeAnalysis.SyntaxToken operatorKeyword, Microsoft.CodeAnalysis.SyntaxToken operatorToken, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.OperatorDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.OperatorDeclarationSyntax.WithExplicitInterfaceSpecifier(Microsoft.CodeAnalysis.CSharp.Syntax.ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.OperatorDeclarationSyntax diff --git a/src/Compilers/CSharp/Portable/Syntax/OperatorDeclarationSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/OperatorDeclarationSyntax.cs new file mode 100644 index 0000000000000..bbfb4f31c77c9 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Syntax/OperatorDeclarationSyntax.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.CSharp.Syntax +{ + public partial class OperatorDeclarationSyntax + { + public OperatorDeclarationSyntax Update( + SyntaxList attributeLists, + SyntaxTokenList modifiers, + TypeSyntax returnType, + SyntaxToken operatorKeyword, + SyntaxToken operatorToken, + ParameterListSyntax parameterList, + BlockSyntax? body, + ArrowExpressionClauseSyntax? expressionBody, + SyntaxToken semicolonToken) + { + return Update( + attributeLists: attributeLists, + modifiers: modifiers, + returnType: returnType, + explicitInterfaceSpecifier: null, + operatorKeyword: operatorKeyword, + operatorToken: operatorToken, + parameterList: parameterList, + body: body, + expressionBody: expressionBody, + semicolonToken: semicolonToken); + } + } +} diff --git a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml index f24059e17aa41..89398039c4a9a 100644 --- a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml +++ b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml @@ -3709,6 +3709,7 @@ Gets the return type. + Gets the "operator" keyword. diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs index 7314c84580886..fb7b507d77a2e 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs @@ -2468,6 +2468,7 @@ public static ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration( semicolonToken: semicolonToken); } + /// Creates a new OperatorDeclarationSyntax instance. public static OperatorDeclarationSyntax OperatorDeclaration( SyntaxList attributeLists, SyntaxTokenList modifiers, @@ -2490,6 +2491,52 @@ public static OperatorDeclarationSyntax OperatorDeclaration( semicolonToken: semicolonToken); } + /// Creates a new OperatorDeclarationSyntax instance. + public static OperatorDeclarationSyntax OperatorDeclaration( + SyntaxList attributeLists, + SyntaxTokenList modifiers, + TypeSyntax returnType, + SyntaxToken operatorKeyword, + SyntaxToken operatorToken, + ParameterListSyntax parameterList, + BlockSyntax? body, + ArrowExpressionClauseSyntax? expressionBody, + SyntaxToken semicolonToken) + { + return SyntaxFactory.OperatorDeclaration( + attributeLists: attributeLists, + modifiers: modifiers, + returnType: returnType, + explicitInterfaceSpecifier: null, + operatorKeyword: operatorKeyword, + operatorToken: operatorToken, + parameterList: parameterList, + body: body, + expressionBody: expressionBody, + semicolonToken: semicolonToken); + } + + /// Creates a new OperatorDeclarationSyntax instance. + public static OperatorDeclarationSyntax OperatorDeclaration( + SyntaxList attributeLists, + SyntaxTokenList modifiers, + TypeSyntax returnType, + SyntaxToken operatorToken, + ParameterListSyntax parameterList, + BlockSyntax? body, + ArrowExpressionClauseSyntax? expressionBody) + { + return SyntaxFactory.OperatorDeclaration( + attributeLists: attributeLists, + modifiers: modifiers, + returnType: returnType, + explicitInterfaceSpecifier: null, + operatorToken: operatorToken, + parameterList: parameterList, + body: body, + expressionBody: expressionBody); + } + /// Creates a new UsingDirectiveSyntax instance. public static UsingDirectiveSyntax UsingDirective(NameEqualsSyntax alias, NameSyntax name) { diff --git a/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs b/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs index fe56c3ae30b27..1b3db9ac1418d 100644 --- a/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs +++ b/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs @@ -530,7 +530,7 @@ private static Syntax.InternalSyntax.MethodDeclarationSyntax GenerateMethodDecla => InternalSyntaxFactory.MethodDeclaration(new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), GenerateIdentifierName(), null, InternalSyntaxFactory.Identifier("Identifier"), null, GenerateParameterList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), null, null, null); private static Syntax.InternalSyntax.OperatorDeclarationSyntax GenerateOperatorDeclaration() - => InternalSyntaxFactory.OperatorDeclaration(new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), GenerateIdentifierName(), InternalSyntaxFactory.Token(SyntaxKind.OperatorKeyword), InternalSyntaxFactory.Token(SyntaxKind.PlusToken), GenerateParameterList(), null, null, null); + => InternalSyntaxFactory.OperatorDeclaration(new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), GenerateIdentifierName(), null, InternalSyntaxFactory.Token(SyntaxKind.OperatorKeyword), InternalSyntaxFactory.Token(SyntaxKind.PlusToken), GenerateParameterList(), null, null, null); private static Syntax.InternalSyntax.ConversionOperatorDeclarationSyntax GenerateConversionOperatorDeclaration() => InternalSyntaxFactory.ConversionOperatorDeclaration(new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), InternalSyntaxFactory.Token(SyntaxKind.ImplicitKeyword), InternalSyntaxFactory.Token(SyntaxKind.OperatorKeyword), GenerateIdentifierName(), GenerateParameterList(), null, null, null); @@ -2891,6 +2891,7 @@ public void TestOperatorDeclarationFactoryAndProperties() Assert.Equal(default, node.AttributeLists); Assert.Equal(default, node.Modifiers); Assert.NotNull(node.ReturnType); + Assert.Null(node.ExplicitInterfaceSpecifier); Assert.Equal(SyntaxKind.OperatorKeyword, node.OperatorKeyword.Kind); Assert.Equal(SyntaxKind.PlusToken, node.OperatorToken.Kind); Assert.NotNull(node.ParameterList); @@ -10193,7 +10194,7 @@ private static MethodDeclarationSyntax GenerateMethodDeclaration() => SyntaxFactory.MethodDeclaration(new SyntaxList(), new SyntaxTokenList(), GenerateIdentifierName(), default(ExplicitInterfaceSpecifierSyntax), SyntaxFactory.Identifier("Identifier"), default(TypeParameterListSyntax), GenerateParameterList(), new SyntaxList(), default(BlockSyntax), default(ArrowExpressionClauseSyntax), default(SyntaxToken)); private static OperatorDeclarationSyntax GenerateOperatorDeclaration() - => SyntaxFactory.OperatorDeclaration(new SyntaxList(), new SyntaxTokenList(), GenerateIdentifierName(), SyntaxFactory.Token(SyntaxKind.OperatorKeyword), SyntaxFactory.Token(SyntaxKind.PlusToken), GenerateParameterList(), default(BlockSyntax), default(ArrowExpressionClauseSyntax), default(SyntaxToken)); + => SyntaxFactory.OperatorDeclaration(new SyntaxList(), new SyntaxTokenList(), GenerateIdentifierName(), default(ExplicitInterfaceSpecifierSyntax), SyntaxFactory.Token(SyntaxKind.OperatorKeyword), SyntaxFactory.Token(SyntaxKind.PlusToken), GenerateParameterList(), default(BlockSyntax), default(ArrowExpressionClauseSyntax), default(SyntaxToken)); private static ConversionOperatorDeclarationSyntax GenerateConversionOperatorDeclaration() => SyntaxFactory.ConversionOperatorDeclaration(new SyntaxList(), new SyntaxTokenList(), SyntaxFactory.Token(SyntaxKind.ImplicitKeyword), SyntaxFactory.Token(SyntaxKind.OperatorKeyword), GenerateIdentifierName(), GenerateParameterList(), default(BlockSyntax), default(ArrowExpressionClauseSyntax), default(SyntaxToken)); @@ -12554,13 +12555,14 @@ public void TestOperatorDeclarationFactoryAndProperties() Assert.Equal(default, node.AttributeLists); Assert.Equal(default, node.Modifiers); Assert.NotNull(node.ReturnType); + Assert.Null(node.ExplicitInterfaceSpecifier); Assert.Equal(SyntaxKind.OperatorKeyword, node.OperatorKeyword.Kind()); Assert.Equal(SyntaxKind.PlusToken, node.OperatorToken.Kind()); Assert.NotNull(node.ParameterList); Assert.Null(node.Body); Assert.Null(node.ExpressionBody); Assert.Equal(SyntaxKind.None, node.SemicolonToken.Kind()); - var newNode = node.WithAttributeLists(node.AttributeLists).WithModifiers(node.Modifiers).WithReturnType(node.ReturnType).WithOperatorKeyword(node.OperatorKeyword).WithOperatorToken(node.OperatorToken).WithParameterList(node.ParameterList).WithBody(node.Body).WithExpressionBody(node.ExpressionBody).WithSemicolonToken(node.SemicolonToken); + var newNode = node.WithAttributeLists(node.AttributeLists).WithModifiers(node.Modifiers).WithReturnType(node.ReturnType).WithExplicitInterfaceSpecifier(node.ExplicitInterfaceSpecifier).WithOperatorKeyword(node.OperatorKeyword).WithOperatorToken(node.OperatorToken).WithParameterList(node.ParameterList).WithBody(node.Body).WithExpressionBody(node.ExpressionBody).WithSemicolonToken(node.SemicolonToken); Assert.Equal(node, newNode); } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs index 50721de54b62f..ad5798b8bb047 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs @@ -24,6 +24,16 @@ private MemberDeclarationSyntax ParseDeclaration(string text, int offset = 0, Pa return SyntaxFactory.ParseMemberDeclaration(text, offset, options); } + private SyntaxTree UsingTree(string text, CSharpParseOptions options, params DiagnosticDescription[] expectedErrors) + { + var tree = base.UsingTree(text, options); + + var actualErrors = tree.GetDiagnostics(); + actualErrors.Verify(expectedErrors); + + return tree; + } + [Fact] [WorkItem(367, "https://github.com/dotnet/roslyn/issues/367")] public void ParsePrivate() @@ -895,5 +905,1509 @@ public void SetAndInitAccessor() EOF(); } } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_01() + { + var error = + // (1,12): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public int N.I.operator +(int x, int y) => x + y; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I.").WithArguments("static abstract members in interfaces").WithLocation(1, 12); + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingDeclaration("public int N.I.operator +(int x, int y) => x + y;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? new[] { error } : new DiagnosticDescription[] { }); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.AddExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.PlusToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_02() + { + var errors = new[] { + // (1,8): error CS1553: Declaration is not valid; use '+ operator (...' instead + // public int N.I.implicit (int x) => x; + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 8), + // (1,16): error CS1003: Syntax error, 'operator' expected + // public int N.I.implicit (int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "implicit").WithArguments("operator", "implicit").WithLocation(1, 16), + // (1,16): error CS1019: Overloadable unary operator expected + // public int N.I.implicit (int x) => x; + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "implicit").WithLocation(1, 16) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingDeclaration("public int N.I.implicit (int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,12): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public int N.I.implicit (int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I.").WithArguments("static abstract members in interfaces").WithLocation(1, 12) + ).ToArray() : + errors); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + M(SyntaxKind.OperatorKeyword); + M(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_03() + { + var errors = new[] { + // (1,8): error CS1553: Declaration is not valid; use '+ operator (...' instead + // public int N.I.explicit (int x) => x; + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 8), + // (1,16): error CS1003: Syntax error, 'operator' expected + // public int N.I.explicit (int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "explicit").WithArguments("operator", "explicit").WithLocation(1, 16), + // (1,16): error CS1019: Overloadable unary operator expected + // public int N.I.explicit (int x) => x; + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "explicit").WithLocation(1, 16) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingDeclaration("public int N.I.explicit (int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,12): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public int N.I.explicit (int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I.").WithArguments("static abstract members in interfaces").WithLocation(1, 12) + ).ToArray() : + errors); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + M(SyntaxKind.OperatorKeyword); + M(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_04() + { + var errors = new[] { + // (1,16): error CS1003: Syntax error, '(' expected + // public int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments("(", "operator").WithLocation(1, 16), + // (1,16): error CS1041: Identifier expected; 'operator' is a keyword + // public int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "operator").WithArguments("", "operator").WithLocation(1, 16), + // (1,32): error CS8124: Tuple must contain at least two elements. + // public int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_TupleTooFewElements, ")").WithLocation(1, 32), + // (1,34): error CS1001: Identifier expected + // public int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_IdentifierExpected, "=>").WithLocation(1, 34), + // (1,34): error CS1003: Syntax error, ',' expected + // public int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "=>").WithArguments(",", "=>").WithLocation(1, 34), + // (1,37): error CS1003: Syntax error, ',' expected + // public int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "x").WithArguments(",", "").WithLocation(1, 37), + // (1,38): error CS1001: Identifier expected + // public int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ";").WithLocation(1, 38), + // (1,38): error CS1026: ) expected + // public int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(1, 38) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingDeclaration("public int N.I operator +(int x) => x;", options: options.WithLanguageVersion(version), errors); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.IdentifierToken, "I"); + N(SyntaxKind.ParameterList); + { + M(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + M(SyntaxKind.CommaToken); + M(SyntaxKind.TupleElement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_05() + { + var errors = new[] { + // (1,14): error CS1003: Syntax error, ',' expected + // public int I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(",", "operator").WithLocation(1, 14) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingDeclaration("public int I operator +(int x) => x;", options: options.WithLanguageVersion(version), errors); + + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_06() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("public int N::I::operator +(int x, int y) => x + y;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,16): error CS7000: Unexpected use of an aliased name + // public int N::I::operator +(int x, int y) => x + y; + Diagnostic(ErrorCode.ERR_UnexpectedAliasedName, "::").WithLocation(1, 16) + ); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.AddExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.PlusToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_07() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("public int I::operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,13): error CS0687: The namespace alias qualifier '::' always resolves to a type or namespace so is illegal here. Consider using '.' instead. + // public int I::operator +(int x) => x; + Diagnostic(ErrorCode.ERR_AliasQualAsExpression, "::").WithLocation(1, 13) + ); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_08() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("public int I.operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview)); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_09() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("public int I.operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview)); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "I"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_10() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("public int N1::N2::I.operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,18): error CS7000: Unexpected use of an aliased name + // public int N1::N2::I.operator +(int x) => x; + Diagnostic(ErrorCode.ERR_UnexpectedAliasedName, "::").WithLocation(1, 18) + ); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N1"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N2"); + } + } + M(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_11() + { + var error = + // (1,12): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public int N.I.operator +(int x, int y) => x + y; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I.").WithArguments("static abstract members in interfaces").WithLocation(1, 12); + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingTree("public int N.I.operator +(int x, int y) => x + y;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? new[] { error } : new DiagnosticDescription[] { }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.AddExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.PlusToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_12() + { + var errors = new[] { + // (1,8): error CS1553: Declaration is not valid; use '+ operator (...' instead + // public int N.I.implicit (int x) => x; + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 8), + // (1,16): error CS1003: Syntax error, 'operator' expected + // public int N.I.implicit (int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "implicit").WithArguments("operator", "implicit").WithLocation(1, 16), + // (1,16): error CS1019: Overloadable unary operator expected + // public int N.I.implicit (int x) => x; + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "implicit").WithLocation(1, 16) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingTree("public int N.I.implicit (int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,12): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public int N.I.implicit (int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I.").WithArguments("static abstract members in interfaces").WithLocation(1, 12) + ).ToArray() : + errors); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + M(SyntaxKind.OperatorKeyword); + M(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_13() + { + var errors = new[] { + // (1,8): error CS1553: Declaration is not valid; use '+ operator (...' instead + // public int N.I.explicit (int x) => x; + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 8), + // (1,16): error CS1003: Syntax error, 'operator' expected + // public int N.I.explicit (int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "explicit").WithArguments("operator", "explicit").WithLocation(1, 16), + // (1,16): error CS1019: Overloadable unary operator expected + // public int N.I.explicit (int x) => x; + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "explicit").WithLocation(1, 16) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingTree("public int N.I.explicit (int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,12): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public int N.I.explicit (int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I.").WithArguments("static abstract members in interfaces").WithLocation(1, 12) + ).ToArray() : + errors); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + M(SyntaxKind.OperatorKeyword); + M(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_14() + { + var errors = new[] { + // (1,16): error CS1003: Syntax error, '(' expected + // public int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments("(", "operator").WithLocation(1, 16), + // (1,16): error CS1041: Identifier expected; 'operator' is a keyword + // public int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "operator").WithArguments("", "operator").WithLocation(1, 16), + // (1,32): error CS8124: Tuple must contain at least two elements. + // public int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_TupleTooFewElements, ")").WithLocation(1, 32), + // (1,34): error CS1001: Identifier expected + // public int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_IdentifierExpected, "=>").WithLocation(1, 34), + // (1,34): error CS1003: Syntax error, ',' expected + // public int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "=>").WithArguments(",", "=>").WithLocation(1, 34), + // (1,37): error CS1003: Syntax error, ',' expected + // public int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "x").WithArguments(",", "").WithLocation(1, 37), + // (1,38): error CS1001: Identifier expected + // public int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ";").WithLocation(1, 38), + // (1,38): error CS1026: ) expected + // public int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(1, 38) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingTree("public int N.I operator +(int x) => x;", options: options.WithLanguageVersion(version), errors); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.IdentifierToken, "I"); + N(SyntaxKind.ParameterList); + { + M(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + M(SyntaxKind.CommaToken); + M(SyntaxKind.TupleElement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_15() + { + { + var errors = new[] { + // (1,14): error CS1003: Syntax error, ',' expected + // public int I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(",", "operator").WithLocation(1, 14) + }; + + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingTree("public int I operator +(int x) => x;", options: TestOptions.Script.WithLanguageVersion(version), errors); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + + { + var errors = new[] { + // (1,14): error CS1002: ; expected + // public int I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "operator").WithLocation(1, 14), + // (1,14): error CS1031: Type expected + // public int I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_TypeExpected, "operator").WithLocation(1, 14) + }; + + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingTree("public int I operator +(int x) => x;", options: TestOptions.Regular.WithLanguageVersion(version), errors); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.OperatorDeclaration); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_16() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingTree("public int N::I::operator +(int x, int y) => x + y;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,16): error CS7000: Unexpected use of an aliased name + // public int N::I::operator +(int x, int y) => x + y; + Diagnostic(ErrorCode.ERR_UnexpectedAliasedName, "::").WithLocation(1, 16) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.AddExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.PlusToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_17() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingTree("public int I::operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,13): error CS0687: The namespace alias qualifier '::' always resolves to a type or namespace so is illegal here. Consider using '.' instead. + // public int I::operator +(int x) => x; + Diagnostic(ErrorCode.ERR_AliasQualAsExpression, "::").WithLocation(1, 13) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_18() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingTree("public int I.operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_19() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingTree("public int I.operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "I"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_20() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingTree("public int N1::N2::I.operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,18): error CS7000: Unexpected use of an aliased name + // public int N1::N2::I.operator +(int x) => x; + Diagnostic(ErrorCode.ERR_UnexpectedAliasedName, "::").WithLocation(1, 18) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N1"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N2"); + } + } + M(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/TopLevelStatementsParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/TopLevelStatementsParsingTests.cs index 6e19aea99dcec..5967e1d75453b 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/TopLevelStatementsParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/TopLevelStatementsParsingTests.cs @@ -17,11 +17,6 @@ public sealed class TopLevelStatementsParsingTests : ParsingTests { public TopLevelStatementsParsingTests(ITestOutputHelper output) : base(output) { } - protected override SyntaxTree ParseTree(string text, CSharpParseOptions options) - { - return SyntaxFactory.ParseSyntaxTree(text, options: options ?? TestOptions.Regular9); - } - private SyntaxTree UsingTree(string text, params DiagnosticDescription[] expectedErrors) { var tree = base.UsingTree(text); From 3c043e47cd1091207e9a8d51b22827498f8177ab Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Tue, 4 May 2021 12:53:22 -0700 Subject: [PATCH 062/127] Add logging to Value Tracking --- .../ValueTracking/ValueTrackingService.cs | 34 +++++++++++++++++-- .../ValueTrackingCommandHandler.cs | 3 ++ .../Compiler/Core/Log/FunctionId.cs | 3 ++ .../Compiler/Core/Log/Logger.cs | 4 +-- 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 26e6fb0b35acd..b0a820f8b87f4 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -32,16 +33,32 @@ public async Task> TrackValueSourceAsync( Document document, CancellationToken cancellationToken) { + using var logger = Logger.LogBlock(FunctionId.ValueTracking_TrackValueSource, cancellationToken, LogLevel.Information); var progressTracker = new ValueTrackingProgressCollector(); - await TrackValueSourceAsync(selection, document, progressTracker, cancellationToken).ConfigureAwait(false); + await TrackValueSourceInternalAsync(selection, document, progressTracker, cancellationToken).ConfigureAwait(false); return progressTracker.GetItems(); } + public async Task TrackValueSourceAsync( TextSpan selection, Document document, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) + { + using var logger = Logger.LogBlock(FunctionId.ValueTracking_TrackValueSource, cancellationToken, LogLevel.Information); + await TrackValueSourceInternalAsync( + selection, + document, + progressCollector, + cancellationToken).ConfigureAwait(false); + } + + private static async Task TrackValueSourceInternalAsync( + TextSpan selection, + Document document, + ValueTrackingProgressCollector progressCollector, + CancellationToken cancellationToken) { var (symbol, node) = await GetSelectedSymbolAsync(selection, document, cancellationToken).ConfigureAwait(false); var operationCollector = new OperationCollector(progressCollector, document.Project.Solution); @@ -95,8 +112,9 @@ public async Task> TrackValueSourceAsync( ValueTrackedItem previousTrackedItem, CancellationToken cancellationToken) { + using var logger = Logger.LogBlock(FunctionId.ValueTracking_TrackValueSource, cancellationToken, LogLevel.Information); var progressTracker = new ValueTrackingProgressCollector(); - await TrackValueSourceAsync(previousTrackedItem, progressTracker, cancellationToken).ConfigureAwait(false); + await TrackValueSourceInternalAsync(previousTrackedItem, progressTracker, cancellationToken).ConfigureAwait(false); return progressTracker.GetItems(); } @@ -104,6 +122,18 @@ public async Task TrackValueSourceAsync( ValueTrackedItem previousTrackedItem, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) + { + using var logger = Logger.LogBlock(FunctionId.ValueTracking_TrackValueSource, cancellationToken, LogLevel.Information); + await TrackValueSourceInternalAsync( + previousTrackedItem, + progressCollector, + cancellationToken).ConfigureAwait(false); + } + + private static async Task TrackValueSourceInternalAsync( + ValueTrackedItem previousTrackedItem, + ValueTrackingProgressCollector progressCollector, + CancellationToken cancellationToken) { progressCollector.Parent = previousTrackedItem; var operationCollector = new OperationCollector(progressCollector, previousTrackedItem.Document.Project.Solution); diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs index e3788f5f20da7..e62db801d00c1 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.ValueTracking; @@ -68,6 +69,8 @@ public CommandState GetCommandState(ValueTrackingEditorCommandArgs args) public bool ExecuteCommand(ValueTrackingEditorCommandArgs args, CommandExecutionContext executionContext) { + using var logger = Logger.LogBlock(FunctionId.ValueTracking_Command, CancellationToken.None, LogLevel.Information); + var cancellationToken = executionContext.OperationContext.UserCancellationToken; var caretPosition = args.TextView.GetCaretPoint(args.SubjectBuffer); if (!caretPosition.HasValue) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs index ad34d84439c63..55aa270ef8559 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs @@ -516,5 +516,8 @@ internal enum FunctionId LSP_CompletionListCacheMiss = 486, VS_ErrorReportingService_ShowGlobalErrorInfo = 489, + + ValueTracking_Command = 490, + ValueTracking_TrackValueSource = 491, } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/Logger.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/Logger.cs index d9c423c7bf8ca..ab10db509a070 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/Logger.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/Logger.cs @@ -198,8 +198,8 @@ private static int GetNextUniqueBlockId() /// /// simplest way to log a start and end pair /// - public static IDisposable LogBlock(FunctionId functionId, CancellationToken token) - => LogBlock(functionId, string.Empty, token); + public static IDisposable LogBlock(FunctionId functionId, CancellationToken token, LogLevel logLevel = LogLevel.Trace) + => LogBlock(functionId, string.Empty, token, logLevel); /// /// simplest way to log a start and end pair with a simple context message which should be very cheap to create From 91d6ebfc2fd234ebfd3c27fded2197d78938dfd2 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Tue, 4 May 2021 13:52:23 -0700 Subject: [PATCH 063/127] Implmenet new IStreamingFindReferencesProgress --- ...eTrackingService.FindReferencesProgress.cs | 52 +++++++++---------- .../ValueTracking/ValueTrackingService.cs | 4 +- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs index c680a7da66702..c355b1eead471 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs @@ -16,29 +16,27 @@ internal partial class ValueTrackingService { private class FindReferencesProgress : IStreamingFindReferencesProgress, IStreamingProgressTracker { - private readonly CancellationToken _cancellationToken; private readonly OperationCollector _operationCollector; - public FindReferencesProgress(OperationCollector valueTrackingProgressCollector, CancellationToken cancellationToken = default) + public FindReferencesProgress(OperationCollector valueTrackingProgressCollector) { _operationCollector = valueTrackingProgressCollector; - _cancellationToken = cancellationToken; } public IStreamingProgressTracker ProgressTracker => this; - public ValueTask AddItemsAsync(int count) => new(); + public ValueTask AddItemsAsync(int count, CancellationToken _) => new(); - public ValueTask ItemCompletedAsync() => new(); + public ValueTask ItemCompletedAsync(CancellationToken _) => new(); - public ValueTask OnCompletedAsync() => new(); + public ValueTask OnCompletedAsync(CancellationToken _) => new(); - public ValueTask OnDefinitionFoundAsync(SymbolGroup symbolGroup) => new(); + public ValueTask OnDefinitionFoundAsync(SymbolGroup symbolGroup, CancellationToken _) => new(); - public ValueTask OnFindInDocumentCompletedAsync(Document document) => new(); + public ValueTask OnFindInDocumentCompletedAsync(Document document, CancellationToken _) => new(); - public ValueTask OnFindInDocumentStartedAsync(Document document) => new(); + public ValueTask OnFindInDocumentStartedAsync(Document document, CancellationToken _) => new(); - public async ValueTask OnReferenceFoundAsync(SymbolGroup _, ISymbol symbol, ReferenceLocation location) + public async ValueTask OnReferenceFoundAsync(SymbolGroup _, ISymbol symbol, ReferenceLocation location, CancellationToken cancellationToken) { if (!location.Location.IsInSource) { @@ -49,13 +47,13 @@ public async ValueTask OnReferenceFoundAsync(SymbolGroup _, ISymbol symbol, Refe { if (methodSymbol.IsConstructor()) { - await TrackConstructorAsync(location).ConfigureAwait(false); + await TrackConstructorAsync(location, cancellationToken).ConfigureAwait(false); } else { // If we're searching for references to a method, we don't want to store the symbol as that method again. Instead // we want to track the invocations and how to follow their source - await TrackMethodInvocationArgumentsAsync(location).ConfigureAwait(false); + await TrackMethodInvocationArgumentsAsync(location, cancellationToken).ConfigureAwait(false); } } else if (location.IsWrittenTo) @@ -74,30 +72,30 @@ public async ValueTask OnReferenceFoundAsync(SymbolGroup _, ISymbol symbol, Refe if (syntaxFacts.IsLeftSideOfAnyAssignment(node)) { - await AddItemsFromAssignmentAsync(location.Document, node, _operationCollector, _cancellationToken).ConfigureAwait(false); + await AddItemsFromAssignmentAsync(location.Document, node, _operationCollector, cancellationToken).ConfigureAwait(false); } else { - var semanticModel = await location.Document.GetRequiredSemanticModelAsync(_cancellationToken).ConfigureAwait(false); - var operation = semanticModel.GetOperation(node, _cancellationToken); + var semanticModel = await location.Document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var operation = semanticModel.GetOperation(node, cancellationToken); if (operation is null) { return; } - await _operationCollector.VisitAsync(operation, _cancellationToken).ConfigureAwait(false); + await _operationCollector.VisitAsync(operation, cancellationToken).ConfigureAwait(false); } } } - public ValueTask OnStartedAsync() => new(); + public ValueTask OnStartedAsync(CancellationToken _) => new(); - private async Task TrackConstructorAsync(ReferenceLocation referenceLocation) + private async Task TrackConstructorAsync(ReferenceLocation referenceLocation, CancellationToken cancellationToken) { var document = referenceLocation.Document; var span = referenceLocation.Location.SourceSpan; - var syntaxRoot = await document.GetRequiredSyntaxRootAsync(_cancellationToken).ConfigureAwait(false); + var syntaxRoot = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var originalNode = syntaxRoot.FindNode(span); if (originalNode is null || originalNode.Parent is null) @@ -105,22 +103,22 @@ private async Task TrackConstructorAsync(ReferenceLocation referenceLocation) return; } - var semanticModel = await document.GetRequiredSemanticModelAsync(_cancellationToken).ConfigureAwait(false); - var operation = semanticModel.GetOperation(originalNode.Parent, _cancellationToken); + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var operation = semanticModel.GetOperation(originalNode.Parent, cancellationToken); if (operation is not IObjectCreationOperation) { return; } - await _operationCollector.VisitAsync(operation, _cancellationToken).ConfigureAwait(false); + await _operationCollector.VisitAsync(operation, cancellationToken).ConfigureAwait(false); } - private async Task TrackMethodInvocationArgumentsAsync(ReferenceLocation referenceLocation) + private async Task TrackMethodInvocationArgumentsAsync(ReferenceLocation referenceLocation, CancellationToken cancellationToken) { var document = referenceLocation.Document; var span = referenceLocation.Location.SourceSpan; - var syntaxRoot = await document.GetRequiredSyntaxRootAsync(_cancellationToken).ConfigureAwait(false); + var syntaxRoot = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var originalNode = syntaxRoot.FindNode(span); if (originalNode is null) @@ -135,14 +133,14 @@ private async Task TrackMethodInvocationArgumentsAsync(ReferenceLocation referen return; } - var semanticModel = await document.GetRequiredSemanticModelAsync(_cancellationToken).ConfigureAwait(false); - var operation = semanticModel.GetOperation(invocationSyntax, _cancellationToken); + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var operation = semanticModel.GetOperation(invocationSyntax, cancellationToken); if (operation is not IInvocationOperation) { return; } - await _operationCollector.VisitAsync(operation, _cancellationToken).ConfigureAwait(false); + await _operationCollector.VisitAsync(operation, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index b0a820f8b87f4..910f2aa925855 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -229,7 +229,7 @@ private static async Task TrackVariableDefinitionsAsync(ISymbol symbol, Operatio private static async Task TrackVariableReferencesAsync(ISymbol symbol, OperationCollector collector, CancellationToken cancellationToken) { - var findReferenceProgressCollector = new FindReferencesProgress(collector, cancellationToken: cancellationToken); + var findReferenceProgressCollector = new FindReferencesProgress(collector); await SymbolFinder.FindReferencesAsync( symbol, collector.Solution, @@ -243,7 +243,7 @@ private static async Task TrackParameterSymbolAsync( CancellationToken cancellationToken) { var containingMethod = (IMethodSymbol)parameterSymbol.ContainingSymbol; - var findReferenceProgressCollector = new FindReferencesProgress(collector, cancellationToken: cancellationToken); + var findReferenceProgressCollector = new FindReferencesProgress(collector); await SymbolFinder.FindReferencesAsync( containingMethod, collector.Solution, From ce69e1fb078f64e6c2418e8262a6a879ce2c6232 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Tue, 4 May 2021 14:21:36 -0700 Subject: [PATCH 064/127] Improve error recovery in parser around explicit interface specifier in operators. (#53142) Also adding a bunch of parsing tests for scenarios without modifiers. --- .../CSharp/Portable/Parser/LanguageParser.cs | 22 +- .../Parsing/MemberDeclarationParsingTests.cs | 1928 +++++++++++++++-- 2 files changed, 1764 insertions(+), 186 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 0c10b0204a253..8bdb6bedac8c7 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -2286,6 +2286,7 @@ private MemberDeclarationSyntax ParseMemberDeclarationOrStatementCore(SyntaxKind try { + // Try as a regualr statement rather than a member declaration, if appropriate. if ((!haveAttributes || !IsScript) && !haveModifiers && (type.Kind == SyntaxKind.RefType || !IsOperatorStart(out _, advanceParser: false))) { this.Reset(ref afterAttributesPoint); @@ -6007,7 +6008,7 @@ private void ParseMemberName( } } - private void AccumulateExplicitInterfaceName(ref NameSyntax explicitInterfaceName, ref SyntaxToken separator, bool reportAnErrorOnMispacedColonColon = false) + private void AccumulateExplicitInterfaceName(ref NameSyntax explicitInterfaceName, ref SyntaxToken separator, bool reportAnErrorOnMisplacedColonColon = false) { // first parse the upcoming name portion. @@ -6038,7 +6039,7 @@ private void AccumulateExplicitInterfaceName(ref NameSyntax explicitInterfaceNam { separator = this.EatToken(); - if (reportAnErrorOnMispacedColonColon) + if (reportAnErrorOnMisplacedColonColon) { // The https://github.com/dotnet/roslyn/issues/53021 is tracking fixing this in general separator = this.AddError(separator, ErrorCode.ERR_UnexpectedAliasedName); @@ -6090,8 +6091,19 @@ private bool IsOperatorStart(out ExplicitInterfaceSpecifierSyntax explicitInterf bool isPartOfInterfaceName; try { - ScanNamedTypePart(); - isPartOfInterfaceName = IsDotOrColonColon(); + if (IsOperatorKeyword()) + { + isPartOfInterfaceName = false; + } + else + { + ScanNamedTypePart(); + + // If we have part of the interface name, but no dot before the operator token, then + // for the purpose of error recovery, treat this as an operator start with a + // missing dot token. + isPartOfInterfaceName = IsDotOrColonColon() || IsOperatorKeyword(); + } } finally { @@ -6113,7 +6125,7 @@ private bool IsOperatorStart(out ExplicitInterfaceSpecifierSyntax explicitInterf else { // If we saw a . or :: then we must have something explicit. - AccumulateExplicitInterfaceName(ref explicitInterfaceName, ref separator, reportAnErrorOnMispacedColonColon: true); + AccumulateExplicitInterfaceName(ref explicitInterfaceName, ref separator, reportAnErrorOnMisplacedColonColon: true); } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs index ad5798b8bb047..fd1a238900384 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs @@ -1157,39 +1157,25 @@ public void OperatorDeclaration_ExplicitImplementation_03() public void OperatorDeclaration_ExplicitImplementation_04() { var errors = new[] { - // (1,16): error CS1003: Syntax error, '(' expected + // (1,16): error CS1003: Syntax error, '.' expected // public int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments("(", "operator").WithLocation(1, 16), - // (1,16): error CS1041: Identifier expected; 'operator' is a keyword - // public int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "operator").WithArguments("", "operator").WithLocation(1, 16), - // (1,32): error CS8124: Tuple must contain at least two elements. - // public int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_TupleTooFewElements, ")").WithLocation(1, 32), - // (1,34): error CS1001: Identifier expected - // public int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_IdentifierExpected, "=>").WithLocation(1, 34), - // (1,34): error CS1003: Syntax error, ',' expected - // public int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_SyntaxError, "=>").WithArguments(",", "=>").WithLocation(1, 34), - // (1,37): error CS1003: Syntax error, ',' expected - // public int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_SyntaxError, "x").WithArguments(",", "").WithLocation(1, 37), - // (1,38): error CS1001: Identifier expected - // public int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_IdentifierExpected, ";").WithLocation(1, 38), - // (1,38): error CS1026: ) expected - // public int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(1, 38) + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".", "operator").WithLocation(1, 16) }; foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingDeclaration("public int N.I operator +(int x) => x;", options: options.WithLanguageVersion(version), errors); + UsingDeclaration("public int N.I operator +(int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,12): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I ").WithArguments("static abstract members in interfaces").WithLocation(1, 12) + ).ToArray() : + errors); - N(SyntaxKind.MethodDeclaration); + N(SyntaxKind.OperatorDeclaration); { N(SyntaxKind.PublicKeyword); N(SyntaxKind.PredefinedType); @@ -1198,51 +1184,42 @@ public void OperatorDeclaration_ExplicitImplementation_04() } N(SyntaxKind.ExplicitInterfaceSpecifier); { - N(SyntaxKind.IdentifierName); + N(SyntaxKind.QualifiedName); { - N(SyntaxKind.IdentifierToken, "N"); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } } - N(SyntaxKind.DotToken); + M(SyntaxKind.DotToken); } - N(SyntaxKind.IdentifierToken, "I"); + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); N(SyntaxKind.ParameterList); { - M(SyntaxKind.OpenParenToken); + N(SyntaxKind.OpenParenToken); N(SyntaxKind.Parameter); { - N(SyntaxKind.TupleType); + N(SyntaxKind.PredefinedType); { - N(SyntaxKind.OpenParenToken); - N(SyntaxKind.TupleElement); - { - N(SyntaxKind.PredefinedType); - { - N(SyntaxKind.IntKeyword); - } - N(SyntaxKind.IdentifierToken, "x"); - } - M(SyntaxKind.CommaToken); - M(SyntaxKind.TupleElement); - { - M(SyntaxKind.IdentifierName); - { - M(SyntaxKind.IdentifierToken); - } - } - N(SyntaxKind.CloseParenToken); + N(SyntaxKind.IntKeyword); } - M(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "x"); } - M(SyntaxKind.CommaToken); - N(SyntaxKind.Parameter); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "x"); - } - M(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "x"); } - M(SyntaxKind.CloseParenToken); } N(SyntaxKind.SemicolonToken); } @@ -1255,29 +1232,60 @@ public void OperatorDeclaration_ExplicitImplementation_04() public void OperatorDeclaration_ExplicitImplementation_05() { var errors = new[] { - // (1,14): error CS1003: Syntax error, ',' expected + // (1,14): error CS1003: Syntax error, '.' expected // public int I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(",", "operator").WithLocation(1, 14) + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".", "operator").WithLocation(1, 14) }; foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingDeclaration("public int I operator +(int x) => x;", options: options.WithLanguageVersion(version), errors); + UsingDeclaration("public int I operator +(int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,12): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public int I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "I ").WithArguments("static abstract members in interfaces").WithLocation(1, 12) + ).ToArray() : + errors); - N(SyntaxKind.FieldDeclaration); + N(SyntaxKind.OperatorDeclaration); { N(SyntaxKind.PublicKeyword); - N(SyntaxKind.VariableDeclaration); + N(SyntaxKind.PredefinedType); { - N(SyntaxKind.PredefinedType); + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.IntKeyword); + N(SyntaxKind.IdentifierToken, "I"); } - N(SyntaxKind.VariableDeclarator); + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); { - N(SyntaxKind.IdentifierToken, "I"); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); } } N(SyntaxKind.SemicolonToken); @@ -1867,41 +1875,27 @@ public void OperatorDeclaration_ExplicitImplementation_13() public void OperatorDeclaration_ExplicitImplementation_14() { var errors = new[] { - // (1,16): error CS1003: Syntax error, '(' expected - // public int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments("(", "operator").WithLocation(1, 16), - // (1,16): error CS1041: Identifier expected; 'operator' is a keyword - // public int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "operator").WithArguments("", "operator").WithLocation(1, 16), - // (1,32): error CS8124: Tuple must contain at least two elements. - // public int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_TupleTooFewElements, ")").WithLocation(1, 32), - // (1,34): error CS1001: Identifier expected - // public int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_IdentifierExpected, "=>").WithLocation(1, 34), - // (1,34): error CS1003: Syntax error, ',' expected - // public int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_SyntaxError, "=>").WithArguments(",", "=>").WithLocation(1, 34), - // (1,37): error CS1003: Syntax error, ',' expected + // (1,16): error CS1003: Syntax error, '.' expected // public int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_SyntaxError, "x").WithArguments(",", "").WithLocation(1, 37), - // (1,38): error CS1001: Identifier expected - // public int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_IdentifierExpected, ";").WithLocation(1, 38), - // (1,38): error CS1026: ) expected - // public int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(1, 38) + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".", "operator").WithLocation(1, 16) }; foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingTree("public int N.I operator +(int x) => x;", options: options.WithLanguageVersion(version), errors); + UsingTree("public int N.I operator +(int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,12): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I ").WithArguments("static abstract members in interfaces").WithLocation(1, 12) + ).ToArray() : + errors); N(SyntaxKind.CompilationUnit); { - N(SyntaxKind.MethodDeclaration); + N(SyntaxKind.OperatorDeclaration); { N(SyntaxKind.PublicKeyword); N(SyntaxKind.PredefinedType); @@ -1910,51 +1904,42 @@ public void OperatorDeclaration_ExplicitImplementation_14() } N(SyntaxKind.ExplicitInterfaceSpecifier); { - N(SyntaxKind.IdentifierName); + N(SyntaxKind.QualifiedName); { - N(SyntaxKind.IdentifierToken, "N"); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } } - N(SyntaxKind.DotToken); + M(SyntaxKind.DotToken); } - N(SyntaxKind.IdentifierToken, "I"); + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); N(SyntaxKind.ParameterList); { - M(SyntaxKind.OpenParenToken); + N(SyntaxKind.OpenParenToken); N(SyntaxKind.Parameter); { - N(SyntaxKind.TupleType); + N(SyntaxKind.PredefinedType); { - N(SyntaxKind.OpenParenToken); - N(SyntaxKind.TupleElement); - { - N(SyntaxKind.PredefinedType); - { - N(SyntaxKind.IntKeyword); - } - N(SyntaxKind.IdentifierToken, "x"); - } - M(SyntaxKind.CommaToken); - M(SyntaxKind.TupleElement); - { - M(SyntaxKind.IdentifierName); - { - M(SyntaxKind.IdentifierToken); - } - } - N(SyntaxKind.CloseParenToken); + N(SyntaxKind.IntKeyword); } - M(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "x"); } - M(SyntaxKind.CommaToken); - N(SyntaxKind.Parameter); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "x"); - } - M(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "x"); } - M(SyntaxKind.CloseParenToken); } N(SyntaxKind.SemicolonToken); } @@ -1968,78 +1953,41 @@ public void OperatorDeclaration_ExplicitImplementation_14() [Fact] public void OperatorDeclaration_ExplicitImplementation_15() { - { - var errors = new[] { - // (1,14): error CS1003: Syntax error, ',' expected + var errors = new[] { + // (1,14): error CS1003: Syntax error, '.' expected // public int I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(",", "operator").WithLocation(1, 14) + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".", "operator").WithLocation(1, 14) }; + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingTree("public int I operator +(int x) => x;", options: TestOptions.Script.WithLanguageVersion(version), errors); + UsingTree("public int I operator +(int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,12): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public int I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "I ").WithArguments("static abstract members in interfaces").WithLocation(1, 12) + ).ToArray() : + errors); N(SyntaxKind.CompilationUnit); { - N(SyntaxKind.FieldDeclaration); + N(SyntaxKind.OperatorDeclaration); { N(SyntaxKind.PublicKeyword); - N(SyntaxKind.VariableDeclaration); + N(SyntaxKind.PredefinedType); { - N(SyntaxKind.PredefinedType); - { - N(SyntaxKind.IntKeyword); - } - N(SyntaxKind.VariableDeclarator); - { - N(SyntaxKind.IdentifierToken, "I"); - } + N(SyntaxKind.IntKeyword); } - N(SyntaxKind.SemicolonToken); - } - N(SyntaxKind.EndOfFileToken); - } - EOF(); - } - } - - { - var errors = new[] { - // (1,14): error CS1002: ; expected - // public int I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_SemicolonExpected, "operator").WithLocation(1, 14), - // (1,14): error CS1031: Type expected - // public int I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_TypeExpected, "operator").WithLocation(1, 14) - }; - - foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) - { - UsingTree("public int I operator +(int x) => x;", options: TestOptions.Regular.WithLanguageVersion(version), errors); - - N(SyntaxKind.CompilationUnit); - { - N(SyntaxKind.FieldDeclaration); - { - N(SyntaxKind.PublicKeyword); - N(SyntaxKind.VariableDeclaration); + N(SyntaxKind.ExplicitInterfaceSpecifier); { - N(SyntaxKind.PredefinedType); - { - N(SyntaxKind.IntKeyword); - } - N(SyntaxKind.VariableDeclarator); + N(SyntaxKind.IdentifierName); { N(SyntaxKind.IdentifierToken, "I"); } - } - M(SyntaxKind.SemicolonToken); - } - N(SyntaxKind.OperatorDeclaration); - { - M(SyntaxKind.IdentifierName); - { - M(SyntaxKind.IdentifierToken); + M(SyntaxKind.DotToken); } N(SyntaxKind.OperatorKeyword); N(SyntaxKind.PlusToken); @@ -2409,5 +2357,1623 @@ public void OperatorDeclaration_ExplicitImplementation_20() EOF(); } } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_21() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("public int I..operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,13): error CS1003: Syntax error, ',' expected + // public int I..operator +(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "..").WithArguments(",", "..").WithLocation(1, 13) + ); + + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_22() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("public int I . . operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,16): error CS1001: Identifier expected + // public int I . . operator +(int x) => x; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ".").WithLocation(1, 16) + ); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_23() + { + var error = + // (1,5): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int N.I.operator +(int x, int y) => x + y; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I.").WithArguments("static abstract members in interfaces").WithLocation(1, 5); + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingDeclaration("int N.I.operator +(int x, int y) => x + y;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? new[] { error } : new DiagnosticDescription[] { }); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.AddExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.PlusToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_24() + { + var errors = new[] { + // (1,1): error CS1553: Declaration is not valid; use '+ operator (...' instead + // int N.I.implicit (int x) => x; + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 1), + // (1,9): error CS1003: Syntax error, 'operator' expected + // int N.I.implicit (int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "implicit").WithArguments("operator", "implicit").WithLocation(1, 9), + // (1,9): error CS1019: Overloadable unary operator expected + // int N.I.implicit (int x) => x; + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "implicit").WithLocation(1, 9) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingDeclaration("int N.I.implicit (int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,5): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int N.I.implicit (int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I.").WithArguments("static abstract members in interfaces").WithLocation(1, 5) + ).ToArray() : + errors); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + M(SyntaxKind.OperatorKeyword); + M(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_25() + { + var errors = new[] { + // (1,1): error CS1553: Declaration is not valid; use '+ operator (...' instead + // int N.I.explicit (int x) => x; + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 1), + // (1,9): error CS1003: Syntax error, 'operator' expected + // int N.I.explicit (int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "explicit").WithArguments("operator", "explicit").WithLocation(1, 9), + // (1,16): error CS1019: Overloadable unary operator expected + // int N.I.explicit (int x) => x; + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "explicit").WithLocation(1, 9) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingDeclaration("int N.I.explicit (int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,5): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int N.I.explicit (int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I.").WithArguments("static abstract members in interfaces").WithLocation(1, 5) + ).ToArray() : + errors); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + M(SyntaxKind.OperatorKeyword); + M(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_26() + { + var errors = new[] { + // (1,9): error CS1003: Syntax error, '.' expected + // int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".", "operator").WithLocation(1, 9) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingDeclaration("int N.I operator +(int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,5): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I ").WithArguments("static abstract members in interfaces").WithLocation(1, 5) + ).ToArray() : + errors); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_27() + { + var errors = new[] { + // (1,7): error CS1003: Syntax error, '.' expected + // int I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".", "operator").WithLocation(1, 7) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingDeclaration("int I operator +(int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,5): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "I ").WithArguments("static abstract members in interfaces").WithLocation(1, 5) + ).ToArray() : + errors); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_28() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("int N::I::operator +(int x, int y) => x + y;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,9): error CS7000: Unexpected use of an aliased name + // int N::I::operator +(int x, int y) => x + y; + Diagnostic(ErrorCode.ERR_UnexpectedAliasedName, "::").WithLocation(1, 9) + ); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.AddExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.PlusToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_29() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("int I::operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,6): error CS0687: The namespace alias qualifier '::' always resolves to a type or namespace so is illegal here. Consider using '.' instead. + // int I::operator +(int x) => x; + Diagnostic(ErrorCode.ERR_AliasQualAsExpression, "::").WithLocation(1, 6) + ); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_30() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("int I.operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview)); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_31() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("int I.operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview)); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "I"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_32() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("int N1::N2::I.operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,11): error CS7000: Unexpected use of an aliased name + // int N1::N2::I.operator +(int x) => x; + Diagnostic(ErrorCode.ERR_UnexpectedAliasedName, "::").WithLocation(1, 11) + ); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N1"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N2"); + } + } + M(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_33() + { + var error = + // (1,5): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int N.I.operator +(int x, int y) => x + y; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I.").WithArguments("static abstract members in interfaces").WithLocation(1, 5); + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingTree("int N.I.operator +(int x, int y) => x + y;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? new[] { error } : new DiagnosticDescription[] { }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.AddExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.PlusToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_34() + { + var errors = new[] { + // (1,1): error CS1553: Declaration is not valid; use '+ operator (...' instead + // int N.I.implicit (int x) => x; + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 1), + // (1,9): error CS1003: Syntax error, 'operator' expected + // int N.I.implicit (int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "implicit").WithArguments("operator", "implicit").WithLocation(1, 9), + // (1,9): error CS1019: Overloadable unary operator expected + // int N.I.implicit (int x) => x; + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "implicit").WithLocation(1, 9) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingTree("int N.I.implicit (int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,5): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int N.I.implicit (int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I.").WithArguments("static abstract members in interfaces").WithLocation(1, 5) + ).ToArray() : + errors); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + M(SyntaxKind.OperatorKeyword); + M(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_35() + { + var errors = new[] { + // (1,1): error CS1553: Declaration is not valid; use '+ operator (...' instead + // int N.I.explicit (int x) => x; + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 1), + // (1,9): error CS1003: Syntax error, 'operator' expected + // int N.I.explicit (int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "explicit").WithArguments("operator", "explicit").WithLocation(1, 9), + // (1,9): error CS1019: Overloadable unary operator expected + // int N.I.explicit (int x) => x; + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "explicit").WithLocation(1, 9) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingTree("int N.I.explicit (int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,5): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int N.I.explicit (int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I.").WithArguments("static abstract members in interfaces").WithLocation(1, 5) + ).ToArray() : + errors); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + M(SyntaxKind.OperatorKeyword); + M(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_36() + { + var errors = new[] { + // (1,9): error CS1003: Syntax error, '.' expected + // int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".", "operator").WithLocation(1, 9) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingTree("int N.I operator +(int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,5): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int N.I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I ").WithArguments("static abstract members in interfaces").WithLocation(1, 5) + ).ToArray() : + errors); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_37() + { + var errors = new[] { + // (1,7): error CS1003: Syntax error, '.' expected + // int I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".", "operator").WithLocation(1, 7) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingTree("int I operator +(int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,5): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int I operator +(int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "I ").WithArguments("static abstract members in interfaces").WithLocation(1, 5) + ).ToArray() : + errors); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_38() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingTree("int N::I::operator +(int x, int y) => x + y;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,9): error CS7000: Unexpected use of an aliased name + // int N::I::operator +(int x, int y) => x + y; + Diagnostic(ErrorCode.ERR_UnexpectedAliasedName, "::").WithLocation(1, 9) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.AddExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.PlusToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_39() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingTree("int I::operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,6): error CS0687: The namespace alias qualifier '::' always resolves to a type or namespace so is illegal here. Consider using '.' instead. + // int I::operator +(int x) => x; + Diagnostic(ErrorCode.ERR_AliasQualAsExpression, "::").WithLocation(1, 6) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_40() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingTree("int I.operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_41() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingTree("int I.operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "I"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_42() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingTree("int N1::N2::I.operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,11): error CS7000: Unexpected use of an aliased name + // int N1::N2::I.operator +(int x) => x; + Diagnostic(ErrorCode.ERR_UnexpectedAliasedName, "::").WithLocation(1, 11) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N1"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N2"); + } + } + M(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_43() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("int I..operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,6): error CS1003: Syntax error, ',' expected + // int I..operator +(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "..").WithArguments(",", "..").WithLocation(1, 6) + ); + + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_44() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("int I . . operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,9): error CS1001: Identifier expected + // int I . . operator +(int x) => x; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ".").WithLocation(1, 9) + ); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } } } From 18007c6bcb4c86da8da627470b363c42e4875654 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 4 May 2021 14:57:28 -0700 Subject: [PATCH 065/127] Update proposal URL in StaticAbstractMembersInInterfaces.md (#53146) --- docs/features/StaticAbstractMembersInInterfaces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/StaticAbstractMembersInInterfaces.md b/docs/features/StaticAbstractMembersInInterfaces.md index c2cec6b47ce3b..ed49e08974677 100644 --- a/docs/features/StaticAbstractMembersInInterfaces.md +++ b/docs/features/StaticAbstractMembersInInterfaces.md @@ -7,7 +7,7 @@ parameters that are constrained by the interface. Proposal: - https://github.com/dotnet/csharplang/issues/4436 -- https://github.com/dotnet/csharplang/blob/main/proposals/statics-in-interfaces.md +- https://github.com/dotnet/csharplang/blob/main/proposals/static-abstracts-in-interfaces.md Feature branch: https://github.com/dotnet/roslyn/tree/features/StaticAbstractMembersInInterfaces From 3de2785a1ae5284b35558a2d86f959a71e623b78 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Tue, 4 May 2021 17:13:00 -0700 Subject: [PATCH 066/127] Correctness fix --- src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 910f2aa925855..5df16af3ae4df 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -39,7 +39,6 @@ public async Task> TrackValueSourceAsync( return progressTracker.GetItems(); } - public async Task TrackValueSourceAsync( TextSpan selection, Document document, From dd2e7be820fa6dfa0738cdb575fe43ccde04646c Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Wed, 5 May 2021 18:35:17 -0700 Subject: [PATCH 067/127] Add progress bar to the top of value tracking --- .../ValueTracking/ValueTrackingService.cs | 10 ++- .../ValueTrackedTreeItemViewModel.cs | 77 +++++++------------ .../Def/ValueTracking/ValueTrackingTree.xaml | 53 ++++++++----- .../ValueTrackingTreeViewModel.cs | 23 +++++- 4 files changed, 90 insertions(+), 73 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 5df16af3ae4df..6d8237da9d3c8 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -161,7 +161,7 @@ private static async Task TrackValueSourceInternalAsync( // In this case, s is being tracked because it contributed to the return of the method. We only // want to track assignments to s that could impact the return rather than tracking the same method // twice. - var isParameterForPreviousTrackedMethod = previousSymbol?.Equals(parameterSymbol.ContainingSymbol) == true; + var isParameterForPreviousTrackedMethod = previousSymbol?.Equals(parameterSymbol.ContainingSymbol, SymbolEqualityComparer.Default) == true; // For Ref or Out parameters, they contribute data across method calls through assignments // within the method. No need to track returns @@ -307,10 +307,14 @@ private static async Task TrackMethodSymbolAsync(IMethodSymbol methodSymbol, Ope // TODO check for Task static bool HasAValueReturn(IMethodSymbol methodSymbol) - => methodSymbol.ReturnType.SpecialType != SpecialType.System_Void; + { + return methodSymbol.ReturnType.SpecialType != SpecialType.System_Void; + } static bool HasAnOutOrRefParam(IMethodSymbol methodSymbol) - => methodSymbol.Parameters.Any(p => p.IsRefOrOut()); + { + return methodSymbol.Parameters.Any(p => p.IsRefOrOut()); + } } private static async Task<(ISymbol?, SyntaxNode?)> GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs index efdb5ede1bf4b..15febb3e50824 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Navigation; @@ -14,8 +10,6 @@ using Microsoft.CodeAnalysis.ValueTracking; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Text.Classification; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.ValueTracking { @@ -82,28 +76,35 @@ private void CalculateChildren() return; } + TreeViewModel.LoadingCount++; _childrenCalculated = true; - CalculateChildrenAsync(CancellationToken.None) - .ContinueWith(task => - { - if (task.Exception is not null) - { - _childrenCalculated = false; - return; - } - - ChildItems.Clear(); - - foreach (var item in task.Result) - { - ChildItems.Add(item); - } - }, - - // Use the UI thread synchronization context for calling back to the UI - // thread to add the tiems - TaskScheduler.FromCurrentSynchronizationContext()); + System.Threading.Tasks.Task.Run(async () => + { + try + { + var items = await _valueTrackingService.TrackValueSourceAsync(_trackedItem, ThreadingContext.DisposalToken).ConfigureAwait(false); + + await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(); + ChildItems.Clear(); + + foreach (var valueTrackedItem in items) + { + ChildItems.Add(new ValueTrackedTreeItemViewModel( + valueTrackedItem, + _solution, + TreeViewModel, + _glyphService, + _valueTrackingService, + ThreadingContext)); + } + } + finally + { + await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(); + TreeViewModel.LoadingCount--; + } + }, ThreadingContext.DisposalToken); } public override void Select() @@ -120,29 +121,7 @@ public override void Select() .WithChangedOption(new OptionKey(NavigationOptions.PreferProvisionalTab), true) .WithChangedOption(new OptionKey(NavigationOptions.ActivateTab), false); - navigationService.TryNavigateToSpan(workspace, Document.Id, _trackedItem.Span, options, ThreadingContext.DisposalToken); - } - - private async Task> CalculateChildrenAsync(CancellationToken cancellationToken) - { - var valueTrackedItems = await _valueTrackingService.TrackValueSourceAsync( - _trackedItem, - cancellationToken).ConfigureAwait(false); - - // TODO: Use pooled item here? - var builder = new List(); - foreach (var valueTrackedItem in valueTrackedItems) - { - builder.Add(new ValueTrackedTreeItemViewModel( - valueTrackedItem, - _solution, - TreeViewModel, - _glyphService, - _valueTrackingService, - ThreadingContext)); - } - - return builder.ToImmutableArray(); + _ = navigationService.TryNavigateToSpan(workspace, Document.Id, _trackedItem.Span, options, ThreadingContext.DisposalToken); } } } diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml index addbc3112e4ba..14a6b5f9372ae 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml @@ -6,7 +6,9 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Microsoft.VisualStudio.LanguageServices.ValueTracking" mc:Ignorable="d" - d:DesignHeight="450" d:DesignWidth="800"> + xmlns:vs="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0" + d:DesignHeight="450" d:DesignWidth="800" + x:Name="control"> @@ -19,24 +21,37 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + SetProperty(ref _selectedItemLine, value); } + private bool _isLoading; + public bool IsLoading + { + get => _isLoading; + private set => SetProperty(ref _isLoading, value); + } + + private int _loadingCount; + public int LoadingCount + { + get => _loadingCount; + set => SetProperty(ref _loadingCount, value); + } + public bool ShowDetails => SelectedItem is not null; public event PropertyChangedEventHandler? PropertyChanged; - public ValueTrackingTreeViewModel(IClassificationFormatMap classificationFormatMap, ClassificationTypeMap classificationTypeMap, IEditorFormatMapService _formatMapService) + public ValueTrackingTreeViewModel(IClassificationFormatMap classificationFormatMap, ClassificationTypeMap classificationTypeMap, IEditorFormatMapService formatMapService) { ClassificationFormatMap = classificationFormatMap; ClassificationTypeMap = classificationTypeMap; - FormatMapService = _formatMapService; + FormatMapService = formatMapService; var properties = FormatMapService.GetEditorFormatMap("text") .GetProperties(ReferenceHighlightTag.TagId); @@ -76,6 +90,11 @@ private void Self_PropertyChanged(object sender, PropertyChangedEventArgs e) SelectedItem?.Select(); } + + if (e.PropertyName == nameof(LoadingCount)) + { + IsLoading = LoadingCount > 0; + } } private void SetProperty(ref T field, T value, [CallerMemberName] string name = "") From e758184630e7ecbaac83b509b2e7a5cfaddbe85c Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Thu, 6 May 2021 08:03:33 -0700 Subject: [PATCH 068/127] Add support for implementations of interface static abstract operators in classes and structures (#53130) This includes support for implicit and explicit implementations. --- .../BinderFactory.BinderFactoryVisitor.cs | 3 +- .../CSharp/Portable/CSharpResources.resx | 3 + .../Compilation/SyntaxTreeSemanticModel.cs | 7 +- .../Declarations/DeclarationTreeBuilder.cs | 14 +- .../CSharp/Portable/Errors/ErrorCode.cs | 1 + .../SymbolDisplayVisitor.Members.cs | 52 +- .../Symbols/Metadata/PE/PEMethodSymbol.cs | 17 +- .../Source/ExplicitInterfaceHelpers.cs | 79 +- .../Source/SourceMemberContainerSymbol.cs | 2 +- .../Source/SourceOrdinaryMethodSymbol.cs | 5 +- .../SourceUserDefinedConversionSymbol.cs | 3 +- .../Source/SourceUserDefinedOperatorSymbol.cs | 19 +- .../SourceUserDefinedOperatorSymbolBase.cs | 49 +- .../SynthesizedRecordEqualityOperatorBase.cs | 2 +- ...dExplicitImplementationForwardingMethod.cs | 4 + .../SynthesizedImplementationMethod.cs | 2 +- .../CSharp/Portable/Symbols/TypeSymbol.cs | 13 + .../Portable/xlf/CSharpResources.cs.xlf | 5 + .../Portable/xlf/CSharpResources.de.xlf | 5 + .../Portable/xlf/CSharpResources.es.xlf | 5 + .../Portable/xlf/CSharpResources.fr.xlf | 5 + .../Portable/xlf/CSharpResources.it.xlf | 5 + .../Portable/xlf/CSharpResources.ja.xlf | 5 + .../Portable/xlf/CSharpResources.ko.xlf | 5 + .../Portable/xlf/CSharpResources.pl.xlf | 5 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 + .../Portable/xlf/CSharpResources.ru.xlf | 5 + .../Portable/xlf/CSharpResources.tr.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 + .../CodeGen/CodeGenInterfaceImplementation.cs | 4 +- .../StaticAbstractMembersInInterfacesTests.cs | 3519 ++++++++++++++++- 32 files changed, 3775 insertions(+), 88 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs index a59338a7047f5..2bcec0b5716a8 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs @@ -448,7 +448,8 @@ private static string GetMethodName(BaseMethodDeclarationSyntax baseMethodDeclar case SyntaxKind.DestructorDeclaration: return WellKnownMemberNames.DestructorName; case SyntaxKind.OperatorDeclaration: - return OperatorFacts.OperatorNameFromDeclaration((OperatorDeclarationSyntax)baseMethodDeclarationSyntax); + var operatorDeclaration = (OperatorDeclarationSyntax)baseMethodDeclarationSyntax; + return ExplicitInterfaceHelpers.GetMemberName(outerBinder, operatorDeclaration.ExplicitInterfaceSpecifier, OperatorFacts.OperatorNameFromDeclaration(operatorDeclaration)); case SyntaxKind.ConversionOperatorDeclaration: return ((ConversionOperatorDeclarationSyntax)baseMethodDeclarationSyntax).ImplicitOrExplicitKeyword.Kind() == SyntaxKind.ImplicitKeyword ? WellKnownMemberNames.ImplicitConversionName diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 63cfe72e4bcce..5b0d9d40341ad 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6645,4 +6645,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ '{0}' cannot implement interface member '{1}' in type '{2}' because the target runtime doesn't support static abstract members in interfaces. + + Explicit implementation of a user-defined operator '{0}' must be declared static + diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index 61f0a48e49946..37b4f2ea40f2b 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -1703,9 +1703,10 @@ private string GetDeclarationName(CSharpSyntaxNode declaration) } case SyntaxKind.OperatorDeclaration: - var operatorDecl = (OperatorDeclarationSyntax)declaration; - - return OperatorFacts.OperatorNameFromDeclaration(operatorDecl); + { + var operatorDecl = (OperatorDeclarationSyntax)declaration; + return GetDeclarationName(declaration, operatorDecl.ExplicitInterfaceSpecifier, OperatorFacts.OperatorNameFromDeclaration(operatorDecl)); + } case SyntaxKind.ConversionOperatorDeclaration: if (((ConversionOperatorDeclarationSyntax)declaration).ImplicitOrExplicitKeyword.Kind() == SyntaxKind.ExplicitKeyword) diff --git a/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs b/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs index 903b69b9125b9..701f7749ece13 100644 --- a/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs +++ b/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs @@ -742,9 +742,19 @@ private static void AddNonTypeMemberNames( case SyntaxKind.OperatorDeclaration: anyNonTypeMembers = true; + + // Member names are exposed via NamedTypeSymbol.MemberNames and are used primarily + // as an acid test to determine whether a more in-depth search of a type is worthwhile. + // We decided that it was reasonable to exclude explicit interface implementations + // from the list of member names. var opDecl = (Syntax.InternalSyntax.OperatorDeclarationSyntax)member; - var name = OperatorFacts.OperatorNameFromDeclaration(opDecl); - set.Add(name); + + if (opDecl.ExplicitInterfaceSpecifier == null) + { + var name = OperatorFacts.OperatorNameFromDeclaration(opDecl); + set.Add(name); + } + break; case SyntaxKind.ConversionOperatorDeclaration: diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index f3cfc8f061ef7..41e11c70e2bfe 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1950,6 +1950,7 @@ internal enum ErrorCode ERR_ExpressionTreeContainsAbstractStaticMemberAccess = 9108, ERR_CloseUnimplementedInterfaceMemberNotStatic = 9109, ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember = 9110, + ERR_ExplicitImplementationOfOperatorsMustBeStatic = 9111, // Note: you will need to re-generate compiler code after adding warnings (eng\generate-compiler-code.cmd) } diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs index 5ab17c17f5e6a..0264bdd7f5136 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs @@ -477,6 +477,22 @@ public override void VisitMethod(IMethodSymbol symbol) case MethodKind.ExplicitInterfaceImplementation: { AddExplicitInterfaceIfRequired(symbol.ExplicitInterfaceImplementations); + + if (!format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) && + symbol.GetSymbol()?.OriginalDefinition is SourceUserDefinedOperatorSymbolBase) + { + var operatorName = symbol.MetadataName; + var lastDotPosition = operatorName.LastIndexOf('.'); + + if (lastDotPosition >= 0) + { + operatorName = operatorName.Substring(lastDotPosition + 1); + } + + addUserDefinedOperatorName(symbol, operatorName); + break; + } + builder.Add(CreatePart(SymbolDisplayPartKind.MethodName, symbol, ExplicitInterfaceHelpers.GetMemberNameWithoutInterfaceName(symbol.Name))); break; @@ -490,21 +506,7 @@ public override void VisitMethod(IMethodSymbol symbol) } else { - AddKeyword(SyntaxKind.OperatorKeyword); - AddSpace(); - if (symbol.MetadataName == WellKnownMemberNames.TrueOperatorName) - { - AddKeyword(SyntaxKind.TrueKeyword); - } - else if (symbol.MetadataName == WellKnownMemberNames.FalseOperatorName) - { - AddKeyword(SyntaxKind.FalseKeyword); - } - else - { - builder.Add(CreatePart(SymbolDisplayPartKind.MethodName, symbol, - SyntaxFacts.GetText(SyntaxFacts.GetOperatorKind(symbol.MetadataName)))); - } + addUserDefinedOperatorName(symbol, symbol.MetadataName); } break; } @@ -638,6 +640,26 @@ void visitFunctionPointerSignature(IMethodSymbol symbol) AddPunctuation(SyntaxKind.GreaterThanToken); } + + void addUserDefinedOperatorName(IMethodSymbol symbol, string operatorName) + { + AddKeyword(SyntaxKind.OperatorKeyword); + AddSpace(); + + if (operatorName == WellKnownMemberNames.TrueOperatorName) + { + AddKeyword(SyntaxKind.TrueKeyword); + } + else if (operatorName == WellKnownMemberNames.FalseOperatorName) + { + AddKeyword(SyntaxKind.FalseKeyword); + } + else + { + builder.Add(CreatePart(SymbolDisplayPartKind.MethodName, symbol, + SyntaxFacts.GetText(SyntaxFacts.GetOperatorKind(operatorName)))); + } + } } private static SymbolDisplayPartKind GetPartKindForConstructorOrDestructor(IMethodSymbol symbol) diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs index af3315940e2ce..d79d9b4333fdd 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs @@ -1174,18 +1174,17 @@ private MethodKind ComputeMethodKind() return MethodKind.DelegateInvoke; } break; - default: - // Note: this is expensive, so check it last - // Note: method being processed may have an explicit method .override but still be - // publicly accessible, the decision here is being made based on the method's name - if (!SyntaxFacts.IsValidIdentifier(this.Name) && !this.ExplicitInterfaceImplementations.IsEmpty) - { - return MethodKind.ExplicitInterfaceImplementation; - } - break; } } + // Note: this is expensive, so check it last + // Note: method being processed may have an explicit method .override but still be + // publicly accessible, the decision here is being made based on the method's name + if (!SyntaxFacts.IsValidIdentifier(this.Name) && !this.ExplicitInterfaceImplementations.IsEmpty) + { + return MethodKind.ExplicitInterfaceImplementation; + } + return MethodKind.Ordinary; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ExplicitInterfaceHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ExplicitInterfaceHelpers.cs index 0102595d08635..594b978d53f02 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ExplicitInterfaceHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ExplicitInterfaceHelpers.cs @@ -143,12 +143,13 @@ public static T SubstituteExplicitInterfaceImplementation(T unsubstitutedProp internal static MethodSymbol FindExplicitlyImplementedMethod( this MethodSymbol implementingMethod, + bool isOperator, TypeSymbol explicitInterfaceType, string interfaceMethodName, ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifierSyntax, BindingDiagnosticBag diagnostics) { - return (MethodSymbol)FindExplicitlyImplementedMember(implementingMethod, explicitInterfaceType, interfaceMethodName, explicitInterfaceSpecifierSyntax, diagnostics); + return (MethodSymbol)FindExplicitlyImplementedMember(implementingMethod, isOperator, explicitInterfaceType, interfaceMethodName, explicitInterfaceSpecifierSyntax, diagnostics); } internal static PropertySymbol FindExplicitlyImplementedProperty( @@ -158,7 +159,7 @@ internal static PropertySymbol FindExplicitlyImplementedProperty( ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifierSyntax, BindingDiagnosticBag diagnostics) { - return (PropertySymbol)FindExplicitlyImplementedMember(implementingProperty, explicitInterfaceType, interfacePropertyName, explicitInterfaceSpecifierSyntax, diagnostics); + return (PropertySymbol)FindExplicitlyImplementedMember(implementingProperty, isOperator: false, explicitInterfaceType, interfacePropertyName, explicitInterfaceSpecifierSyntax, diagnostics); } internal static EventSymbol FindExplicitlyImplementedEvent( @@ -168,11 +169,12 @@ internal static EventSymbol FindExplicitlyImplementedEvent( ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifierSyntax, BindingDiagnosticBag diagnostics) { - return (EventSymbol)FindExplicitlyImplementedMember(implementingEvent, explicitInterfaceType, interfaceEventName, explicitInterfaceSpecifierSyntax, diagnostics); + return (EventSymbol)FindExplicitlyImplementedMember(implementingEvent, isOperator: false, explicitInterfaceType, interfaceEventName, explicitInterfaceSpecifierSyntax, diagnostics); } private static Symbol FindExplicitlyImplementedMember( Symbol implementingMember, + bool isOperator, TypeSymbol explicitInterfaceType, string interfaceMemberName, ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifierSyntax, @@ -232,55 +234,64 @@ private static Symbol FindExplicitlyImplementedMember( //do a lookup anyway } - // Do not look in itself - if (containingType == (object)explicitInterfaceNamedType.OriginalDefinition) - { - // An error will be reported elsewhere. - // Either the interface is not implemented, or it causes a cycle in the interface hierarchy. - return null; - } - - var hasParamsParam = implementingMember.HasParamsParameter(); - // Setting this flag to true does not imply that an interface member has been successfully implemented. // It just indicates that a corresponding interface member has been found (there may still be errors). var foundMatchingMember = false; Symbol implementedMember = null; - foreach (Symbol interfaceMember in explicitInterfaceNamedType.GetMembers(interfaceMemberName)) + if (!implementingMember.IsStatic || !containingType.IsInterface) { - // At this point, we know that explicitInterfaceNamedType is an interface. - // However, metadata interface members can be static - we ignore them, as does Dev10. - if (interfaceMember.Kind != implementingMember.Kind || !interfaceMember.IsImplementableInterfaceMember()) + // Do not look in itself + if (containingType == (object)explicitInterfaceNamedType.OriginalDefinition) { - continue; + // An error will be reported elsewhere. + // Either the interface is not implemented, or it causes a cycle in the interface hierarchy. + return null; } - if (MemberSignatureComparer.ExplicitImplementationComparer.Equals(implementingMember, interfaceMember)) + var hasParamsParam = implementingMember.HasParamsParameter(); + + foreach (Symbol interfaceMember in explicitInterfaceNamedType.GetMembers(interfaceMemberName)) { - foundMatchingMember = true; - // Cannot implement accessor directly unless - // the accessor is from an indexed property. - if (interfaceMember.IsAccessor() && !((MethodSymbol)interfaceMember).IsIndexedPropertyAccessor()) + // At this point, we know that explicitInterfaceNamedType is an interface. + // However, metadata interface members can be static - we ignore them, as does Dev10. + if (interfaceMember.Kind != implementingMember.Kind || !interfaceMember.IsImplementableInterfaceMember()) { - diagnostics.Add(ErrorCode.ERR_ExplicitMethodImplAccessor, memberLocation, implementingMember, interfaceMember); + continue; } - else + + if (interfaceMember is MethodSymbol interfaceMethod && + (interfaceMethod.MethodKind is MethodKind.UserDefinedOperator or MethodKind.Conversion) != isOperator) { - if (interfaceMember.MustCallMethodsDirectly()) + continue; + } + + if (MemberSignatureComparer.ExplicitImplementationComparer.Equals(implementingMember, interfaceMember)) + { + foundMatchingMember = true; + // Cannot implement accessor directly unless + // the accessor is from an indexed property. + if (interfaceMember.IsAccessor() && !((MethodSymbol)interfaceMember).IsIndexedPropertyAccessor()) { - diagnostics.Add(ErrorCode.ERR_BogusExplicitImpl, memberLocation, implementingMember, interfaceMember); + diagnostics.Add(ErrorCode.ERR_ExplicitMethodImplAccessor, memberLocation, implementingMember, interfaceMember); } - else if (hasParamsParam && !interfaceMember.HasParamsParameter()) + else { - // Note: no error for !hasParamsParam && interfaceMethod.HasParamsParameter() - // Still counts as an implementation. - diagnostics.Add(ErrorCode.ERR_ExplicitImplParams, memberLocation, implementingMember, interfaceMember); - } + if (interfaceMember.MustCallMethodsDirectly()) + { + diagnostics.Add(ErrorCode.ERR_BogusExplicitImpl, memberLocation, implementingMember, interfaceMember); + } + else if (hasParamsParam && !interfaceMember.HasParamsParameter()) + { + // Note: no error for !hasParamsParam && interfaceMethod.HasParamsParameter() + // Still counts as an implementation. + diagnostics.Add(ErrorCode.ERR_ExplicitImplParams, memberLocation, implementingMember, interfaceMember); + } - implementedMember = interfaceMember; - break; + implementedMember = interfaceMember; + break; + } } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index ab98d2b6b5810..07fb169aba48c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -4352,7 +4352,7 @@ private void AddNonTypeMembers( } var method = SourceUserDefinedOperatorSymbol.CreateUserDefinedOperatorSymbol( - this, operatorSyntax, compilation.IsNullableAnalysisEnabledIn(operatorSyntax), diagnostics); + this, bodyBinder, operatorSyntax, compilation.IsNullableAnalysisEnabledIn(operatorSyntax), diagnostics); builder.NonTypeMembers.Add(method); } break; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs index dd2601b351df1..cb45ab6dc057b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs @@ -57,8 +57,7 @@ public static SourceOrdinaryMethodSymbol CreateMethodSymbol( var nameToken = syntax.Identifier; TypeSymbol explicitInterfaceType; - string discardedAliasQualifier; - var name = ExplicitInterfaceHelpers.GetMemberNameAndInterfaceSymbol(bodyBinder, interfaceSpecifier, nameToken.ValueText, diagnostics, out explicitInterfaceType, out discardedAliasQualifier); + var name = ExplicitInterfaceHelpers.GetMemberNameAndInterfaceSymbol(bodyBinder, interfaceSpecifier, nameToken.ValueText, diagnostics, out explicitInterfaceType, aliasQualifierOpt: out _); var location = new SourceLocation(nameToken); var methodKind = interfaceSpecifier == null @@ -276,7 +275,7 @@ protected override void ExtensionMethodChecks(BindingDiagnosticBag diagnostics) protected override MethodSymbol FindExplicitlyImplementedMethod(BindingDiagnosticBag diagnostics) { var syntax = GetSyntax(); - return this.FindExplicitlyImplementedMethod(_explicitInterfaceType, syntax.Identifier.ValueText, syntax.ExplicitInterfaceSpecifier, diagnostics); + return this.FindExplicitlyImplementedMethod(isOperator: false, _explicitInterfaceType, syntax.Identifier.ValueText, syntax.ExplicitInterfaceSpecifier, diagnostics); } protected override Location ReturnTypeLocation => GetSyntax().ReturnType.Location; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs index 937bd94ce0f7a..2927f0d007121 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs @@ -42,11 +42,12 @@ private SourceUserDefinedConversionSymbol( BindingDiagnosticBag diagnostics) : base( MethodKind.Conversion, + explicitInterfaceType: null, name, containingType, location, syntax, - MakeDeclarationModifiers(containingType.IsInterface, syntax, location, diagnostics), + MakeDeclarationModifiers(MethodKind.Conversion, containingType.IsInterface, syntax, location, diagnostics), hasBody: syntax.HasAnyBody(), isExpressionBodied: syntax.Body == null && syntax.ExpressionBody != null, isIterator: SyntaxFacts.HasYieldOperations(syntax.Body), diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs index 155a67e773e4c..aa2cc845bf1d8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs @@ -16,6 +16,7 @@ internal sealed class SourceUserDefinedOperatorSymbol : SourceUserDefinedOperato { public static SourceUserDefinedOperatorSymbol CreateUserDefinedOperatorSymbol( SourceMemberContainerTypeSymbol containingType, + Binder bodyBinder, OperatorDeclarationSyntax syntax, bool isNullableAnalysisEnabled, BindingDiagnosticBag diagnostics) @@ -24,27 +25,39 @@ public static SourceUserDefinedOperatorSymbol CreateUserDefinedOperatorSymbol( string name = OperatorFacts.OperatorNameFromDeclaration(syntax); + var interfaceSpecifier = syntax.ExplicitInterfaceSpecifier; + + TypeSymbol explicitInterfaceType; + name = ExplicitInterfaceHelpers.GetMemberNameAndInterfaceSymbol(bodyBinder, interfaceSpecifier, name, diagnostics, out explicitInterfaceType, aliasQualifierOpt: out _); + + var methodKind = interfaceSpecifier == null + ? MethodKind.UserDefinedOperator + : MethodKind.ExplicitInterfaceImplementation; + return new SourceUserDefinedOperatorSymbol( - containingType, name, location, syntax, isNullableAnalysisEnabled, diagnostics); + methodKind, containingType, explicitInterfaceType, name, location, syntax, isNullableAnalysisEnabled, diagnostics); } // NOTE: no need to call WithUnsafeRegionIfNecessary, since the signature // is bound lazily using binders from a BinderFactory (which will already include an // UnsafeBinder, if necessary). private SourceUserDefinedOperatorSymbol( + MethodKind methodKind, SourceMemberContainerTypeSymbol containingType, + TypeSymbol explicitInterfaceType, string name, Location location, OperatorDeclarationSyntax syntax, bool isNullableAnalysisEnabled, BindingDiagnosticBag diagnostics) : base( - MethodKind.UserDefinedOperator, + methodKind, + explicitInterfaceType, name, containingType, location, syntax, - MakeDeclarationModifiers(containingType.IsInterface, syntax, location, diagnostics), + MakeDeclarationModifiers(methodKind, containingType.IsInterface, syntax, location, diagnostics), hasBody: syntax.HasAnyBody(), isExpressionBodied: syntax.Body == null && syntax.ExpressionBody != null, isIterator: SyntaxFacts.HasYieldOperations(syntax.Body), diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs index d40186e3cd2ea..6bbc3653c2bce 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs @@ -16,9 +16,13 @@ internal abstract class SourceUserDefinedOperatorSymbolBase : SourceOrdinaryMeth private const TypeCompareKind ComparisonForUserDefinedOperators = TypeCompareKind.IgnoreTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes; private readonly string _name; private readonly bool _isExpressionBodied; +#nullable enable + private readonly TypeSymbol? _explicitInterfaceType; +#nullable disable protected SourceUserDefinedOperatorSymbolBase( MethodKind methodKind, + TypeSymbol explicitInterfaceType, string name, SourceMemberContainerTypeSymbol containingType, Location location, @@ -31,6 +35,7 @@ protected SourceUserDefinedOperatorSymbolBase( BindingDiagnosticBag diagnostics) : base(containingType, syntax.GetReference(), location, isIterator: isIterator) { + _explicitInterfaceType = explicitInterfaceType; _name = name; _isExpressionBodied = isExpressionBodied; @@ -61,7 +66,14 @@ protected SourceUserDefinedOperatorSymbolBase( // SPEC: An operator declaration must include both a public and a // SPEC: static modifier - if (this.DeclaredAccessibility != Accessibility.Public || !this.IsStatic) + if (this.IsExplicitInterfaceImplementation) + { + if (!this.IsStatic) + { + diagnostics.Add(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, this.Locations[0], this); + } + } + else if (this.DeclaredAccessibility != Accessibility.Public || !this.IsStatic) { // CS0558: User-defined operator '...' must be declared static and public diagnostics.Add(ErrorCode.ERR_OperatorsMustBeStatic, this.Locations[0], this); @@ -104,18 +116,23 @@ protected SourceUserDefinedOperatorSymbolBase( } } - protected static DeclarationModifiers MakeDeclarationModifiers(bool inInterface, BaseMethodDeclarationSyntax syntax, Location location, BindingDiagnosticBag diagnostics) + protected static DeclarationModifiers MakeDeclarationModifiers(MethodKind methodKind, bool inInterface, BaseMethodDeclarationSyntax syntax, Location location, BindingDiagnosticBag diagnostics) { - var defaultAccess = inInterface ? DeclarationModifiers.Public : DeclarationModifiers.Private; + bool isExplicitInterfaceImplementation = methodKind == MethodKind.ExplicitInterfaceImplementation; + var defaultAccess = inInterface && !isExplicitInterfaceImplementation ? DeclarationModifiers.Public : DeclarationModifiers.Private; var allowedModifiers = - DeclarationModifiers.AccessibilityMask | DeclarationModifiers.Static | DeclarationModifiers.Extern | DeclarationModifiers.Unsafe; - if (inInterface) + if (!isExplicitInterfaceImplementation) { - allowedModifiers |= DeclarationModifiers.Abstract | DeclarationModifiers.Sealed; + allowedModifiers |= DeclarationModifiers.AccessibilityMask; + + if (inInterface) + { + allowedModifiers |= DeclarationModifiers.Abstract | DeclarationModifiers.Sealed; + } } var result = ModifierUtils.MakeAndCheckNontypeMemberModifiers( @@ -247,10 +264,16 @@ protected sealed override void ExtensionMethodChecks(BindingDiagnosticBag diagno protected sealed override MethodSymbol FindExplicitlyImplementedMethod(BindingDiagnosticBag diagnostics) { + if (_explicitInterfaceType is object) + { + var syntax = (OperatorDeclarationSyntax)syntaxReferenceOpt.GetSyntax(); + return this.FindExplicitlyImplementedMethod(isOperator: true, _explicitInterfaceType, OperatorFacts.OperatorNameFromDeclaration(syntax), syntax.ExplicitInterfaceSpecifier, diagnostics); + } + return null; } - protected sealed override TypeSymbol ExplicitInterfaceType => null; + protected sealed override TypeSymbol ExplicitInterfaceType => _explicitInterfaceType; private void CheckValueParameters(BindingDiagnosticBag diagnostics) { @@ -267,6 +290,12 @@ private void CheckValueParameters(BindingDiagnosticBag diagnostics) private void CheckOperatorSignatures(BindingDiagnosticBag diagnostics) { + if (MethodKind == MethodKind.ExplicitInterfaceImplementation) + { + // The signature is driven by the interface + return; + } + // Have we even got the right formal parameter arity? If not then // we are in an error recovery scenario and we should just bail // out immediately. @@ -713,6 +742,12 @@ internal sealed override bool IsExpressionBodied protected sealed override void CheckConstraintsForExplicitInterfaceType(ConversionsBase conversions, BindingDiagnosticBag diagnostics) { + if ((object)_explicitInterfaceType != null) + { + var syntax = (OperatorDeclarationSyntax)syntaxReferenceOpt.GetSyntax(); + Debug.Assert(syntax.ExplicitInterfaceSpecifier != null); + _explicitInterfaceType.CheckAllConstraints(DeclaringCompilation, conversions, new SourceLocation(syntax.ExplicitInterfaceSpecifier.Name), diagnostics); + } } protected sealed override void PartialMethodChecks(BindingDiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityOperatorBase.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityOperatorBase.cs index 205e01c058cea..3a278bf01d229 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityOperatorBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityOperatorBase.cs @@ -27,7 +27,7 @@ internal abstract class SynthesizedRecordEqualityOperatorBase : SourceUserDefine private readonly int _memberOffset; protected SynthesizedRecordEqualityOperatorBase(SourceMemberContainerTypeSymbol containingType, string name, int memberOffset, BindingDiagnosticBag diagnostics) - : base(MethodKind.UserDefinedOperator, name, containingType, containingType.Locations[0], (CSharpSyntaxNode)containingType.SyntaxReferences[0].GetSyntax(), + : base(MethodKind.UserDefinedOperator, explicitInterfaceType: null, name, containingType, containingType.Locations[0], (CSharpSyntaxNode)containingType.SyntaxReferences[0].GetSyntax(), DeclarationModifiers.Public | DeclarationModifiers.Static, hasBody: true, isExpressionBodied: false, isIterator: false, isNullableAnalysisEnabled: false, diagnostics) { Debug.Assert(name == WellKnownMemberNames.EqualityOperatorName || name == WellKnownMemberNames.InequalityOperatorName); diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedExplicitImplementationForwardingMethod.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedExplicitImplementationForwardingMethod.cs index 2f0221466088f..58a2f15cc2eae 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedExplicitImplementationForwardingMethod.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedExplicitImplementationForwardingMethod.cs @@ -46,5 +46,9 @@ public override MethodKind MethodKind } public override bool IsStatic => _implementingMethod.IsStatic; + + internal override bool HasSpecialName => false; + + internal sealed override bool HasRuntimeSpecialName => false; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedImplementationMethod.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedImplementationMethod.cs index 41804afd43139..23fe785515aa3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedImplementationMethod.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedImplementationMethod.cs @@ -208,7 +208,7 @@ public override string Name get { return _name; } } - internal sealed override bool HasSpecialName + internal override bool HasSpecialName { get { return _interfaceMethod.HasSpecialName; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs index 83aa4fc8b29f7..1e1248069fdcb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs @@ -2019,10 +2019,23 @@ private static void FindPotentialImplicitImplementationMemberDeclaredInType( implicitImpl = null; closeMismatch = null; + bool? isOperator = null; + + if (interfaceMember is MethodSymbol interfaceMethod) + { + isOperator = interfaceMethod.MethodKind is MethodKind.UserDefinedOperator or MethodKind.Conversion; + } + foreach (Symbol member in currType.GetMembers(interfaceMember.Name)) { if (member.Kind == interfaceMember.Kind) { + if (isOperator.HasValue && + (((MethodSymbol)member).MethodKind is MethodKind.UserDefinedOperator or MethodKind.Conversion) != isOperator.GetValueOrDefault()) + { + continue; + } + if (IsInterfaceMemberImplementation(member, interfaceMember, implementingTypeIsFromSomeCompilation)) { implicitImpl = member; diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 62b7a5e5c06b8..6bb3e13371a9f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -357,6 +357,11 @@ Vlastnost kontraktu rovnosti záznamu {0} musí mít přístupový objekt get. + + Explicit implementation of a user-defined operator '{0}' must be declared static + Explicit implementation of a user-defined operator '{0}' must be declared static + + Explicit application of 'System.Runtime.CompilerServices.NullableAttribute' is not allowed. Explicitní použití System.Runtime.CompilerServices.NullableAttribute není povolené. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 4d1138ba542cf..a474d9bf27abc 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -357,6 +357,11 @@ Die EqualityContract-Eigenschaft "{0}" für Datensätze muss eine get-Zugriffsmethode aufweisen. + + Explicit implementation of a user-defined operator '{0}' must be declared static + Explicit implementation of a user-defined operator '{0}' must be declared static + + Explicit application of 'System.Runtime.CompilerServices.NullableAttribute' is not allowed. Die explizite Anwendung von "System.Runtime.CompilerServices.NullableAttribute" ist unzulässig. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 2adb89e18ee4e..4c3acde1cb82e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -357,6 +357,11 @@ La propiedad del contrato de igualdad de registros "{0}" debe tener un descriptor de acceso get. + + Explicit implementation of a user-defined operator '{0}' must be declared static + Explicit implementation of a user-defined operator '{0}' must be declared static + + Explicit application of 'System.Runtime.CompilerServices.NullableAttribute' is not allowed. No se permite la aplicación explícita de "System.Runtime.CompilerServices.NullableAttribute". diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index bb43bae45d41f..364b328c09fd5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -357,6 +357,11 @@ La propriété de contrat d'égalité d'enregistrement '{0}' doit avoir un accesseur get. + + Explicit implementation of a user-defined operator '{0}' must be declared static + Explicit implementation of a user-defined operator '{0}' must be declared static + + Explicit application of 'System.Runtime.CompilerServices.NullableAttribute' is not allowed. L'application explicite de 'System.Runtime.CompilerServices.NullableAttribute' n'est pas autorisée. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 1ef6ea0364cc9..a141228244026 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -357,6 +357,11 @@ La proprietà '{0}' del contratto di uguaglianza record deve contenere una funzione di accesso get. + + Explicit implementation of a user-defined operator '{0}' must be declared static + Explicit implementation of a user-defined operator '{0}' must be declared static + + Explicit application of 'System.Runtime.CompilerServices.NullableAttribute' is not allowed. L'applicazione esplicita di 'System.Runtime.CompilerServices.NullableAttribute' non è consentita. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 8a0db8c60ae07..6465eec000703 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -357,6 +357,11 @@ レコードの等値コントラクト プロパティ '{0}' には get アクセサーが必要です。 + + Explicit implementation of a user-defined operator '{0}' must be declared static + Explicit implementation of a user-defined operator '{0}' must be declared static + + Explicit application of 'System.Runtime.CompilerServices.NullableAttribute' is not allowed. 'System.Runtime.CompilerServices.NullableAttribute' の明示的な適用は許可されていません。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 41cb51667e674..5901cd75fb66e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -357,6 +357,11 @@ 레코드 같음 계약 속성 '{0}'에는 get 접근자가 있어야 합니다. + + Explicit implementation of a user-defined operator '{0}' must be declared static + Explicit implementation of a user-defined operator '{0}' must be declared static + + Explicit application of 'System.Runtime.CompilerServices.NullableAttribute' is not allowed. 'System.Runtime.CompilerServices.NullableAttribute'의 명시적 적용은 허용되지 않습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 365221505f671..822216e40f1bf 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -357,6 +357,11 @@ Właściwość kontraktu równości rekordu „{0}” musi mieć metodę dostępu get. + + Explicit implementation of a user-defined operator '{0}' must be declared static + Explicit implementation of a user-defined operator '{0}' must be declared static + + Explicit application of 'System.Runtime.CompilerServices.NullableAttribute' is not allowed. Jawne stosowanie elementu „System.Runtime.CompilerServices.NullableAttribute” jest niedozwolone. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 98eea0d3accb5..23d3c941d0e1d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -357,6 +357,11 @@ A propriedade de contrato de igualdade do registro '{0}' precisa ter um acessador get. + + Explicit implementation of a user-defined operator '{0}' must be declared static + Explicit implementation of a user-defined operator '{0}' must be declared static + + Explicit application of 'System.Runtime.CompilerServices.NullableAttribute' is not allowed. Não é permitida a aplicação explícita de 'System.Runtime.CompilerServices.NullableAttribute'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index a75a616e548d7..8cc8e19b6ebc9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -357,6 +357,11 @@ Свойство контракта на равенство записей "{0}" должно иметь метод доступа get. + + Explicit implementation of a user-defined operator '{0}' must be declared static + Explicit implementation of a user-defined operator '{0}' must be declared static + + Explicit application of 'System.Runtime.CompilerServices.NullableAttribute' is not allowed. Явное применение атрибута "System.Runtime.CompilerServices.NullableAttribute" не допускается. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 6631c9384a3b5..03ac3dd3f2eff 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -357,6 +357,11 @@ '{0}' kayıt eşitlik anlaşması özelliğinin get erişimcisine sahip olması gerekir. + + Explicit implementation of a user-defined operator '{0}' must be declared static + Explicit implementation of a user-defined operator '{0}' must be declared static + + Explicit application of 'System.Runtime.CompilerServices.NullableAttribute' is not allowed. Açık 'System.Runtime.CompilerServices.NullableAttribute' uygulamasına izin verilmiyor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 95d310a6e5c99..b4881e884eb53 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -357,6 +357,11 @@ 记录等同性合同属性“{0}”必须具有 get 访问器。 + + Explicit implementation of a user-defined operator '{0}' must be declared static + Explicit implementation of a user-defined operator '{0}' must be declared static + + Explicit application of 'System.Runtime.CompilerServices.NullableAttribute' is not allowed. 不允许显示应用 “System.Runtime.CompilerServices.NullableAttribute”。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 3a9577c80febc..e72939f84c247 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -357,6 +357,11 @@ 記錄相等 contract 屬性 '{0}' 必須要有 get 存取子。 + + Explicit implementation of a user-defined operator '{0}' must be declared static + Explicit implementation of a user-defined operator '{0}' must be declared static + + Explicit application of 'System.Runtime.CompilerServices.NullableAttribute' is not allowed. 不允許明確應用 'System.Runtime.CompilerServices.NullableAttribute'。 diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInterfaceImplementation.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInterfaceImplementation.cs index eaff9bc547594..9c5906d8aa7b5 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInterfaceImplementation.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInterfaceImplementation.cs @@ -771,7 +771,7 @@ public static void Main() Signature("Derived", "set_Property", ".method public hidebysig specialname instance System.Void set_Property(System.String value) cil managed"), // Stubs in Derived3 "call" corresponding members in Derived above Signature("Derived3", "Interface.Method", ".method private hidebysig newslot virtual final instance System.Void Interface.Method() cil managed"), - Signature("Derived3", "Interface.set_Property", ".method private hidebysig newslot specialname virtual final instance System.Void Interface.set_Property(System.String value) cil managed"), + Signature("Derived3", "Interface.set_Property", ".method private hidebysig newslot virtual final instance System.Void Interface.set_Property(System.String value) cil managed"), }); comp.VerifyDiagnostics(); // No errors @@ -869,7 +869,7 @@ public static void Main() Signature("Derived", "set_Property", ".method public hidebysig specialname instance System.Void set_Property(System.String value) cil managed"), // Stubs in Derived3 "call" corresponding members in Derived above Signature("Derived3", "Interface.Method", ".method private hidebysig newslot virtual final instance System.Void Interface.Method() cil managed"), - Signature("Derived3", "Interface.set_Property", ".method private hidebysig newslot specialname virtual final instance System.Void Interface.set_Property(System.String value) cil managed") + Signature("Derived3", "Interface.set_Property", ".method private hidebysig newslot virtual final instance System.Void Interface.set_Property(System.String value) cil managed") }); comp.VerifyDiagnostics(); // No errors diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index 535987b94cce4..c24b2e7ef1543 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -11164,6 +11164,11 @@ interface I7 : I1 { abstract static void M01(); } + +interface I8 : I1 +{ + abstract static void I1.M01(); +} "; var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, @@ -11188,7 +11193,13 @@ interface I7 : I1 Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("I6.M01()").WithLocation(27, 20), // (32,26): warning CS0108: 'I7.M01()' hides inherited member 'I1.M01()'. Use the new keyword if hiding was intended. // abstract static void M01(); - Diagnostic(ErrorCode.WRN_NewRequired, "M01").WithArguments("I7.M01()", "I1.M01()").WithLocation(32, 26) + Diagnostic(ErrorCode.WRN_NewRequired, "M01").WithArguments("I7.M01()", "I1.M01()").WithLocation(32, 26), + // (37,29): error CS0106: The modifier 'static' is not valid for this item + // abstract static void I1.M01(); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M01").WithArguments("static").WithLocation(37, 29), + // (37,29): error CS0539: 'I8.M01()' in explicit interface declaration is not found among members of the interface that can be implemented + // abstract static void I1.M01(); + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("I8.M01()").WithLocation(37, 29) ); var m01 = compilation1.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); @@ -11199,6 +11210,7 @@ interface I7 : I1 Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I5").FindImplementationForInterfaceMember(m01)); Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I6").FindImplementationForInterfaceMember(m01)); Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I7").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I8").FindImplementationForInterfaceMember(m01)); } [Theory] @@ -11212,6 +11224,7 @@ public void ImplementAbstractStaticMethod_04(bool structure) public interface I1 { abstract static void M01(); + abstract static void M02(); } "; var source2 = @@ -11219,6 +11232,7 @@ public interface I1 Test: I1 { static void I1.M01() {} + public static void M02() {} } "; var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, @@ -11244,9 +11258,12 @@ static void I1.M01() {} // (4,20): error CS8703: The modifier 'static' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. // static void I1.M01() {} Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("static", "9.0", "preview").WithLocation(4, 20), - // (9,26): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // (10,26): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. // abstract static void M01(); - Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "9.0", "preview").WithLocation(9, 26) + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "9.0", "preview").WithLocation(10, 26), + // (11,26): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static void M02(); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M02").WithArguments("abstract", "9.0", "preview").WithLocation(11, 26) ); } @@ -11384,6 +11401,8 @@ void validate(ModuleSymbol module) var m01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); var c = module.GlobalNamespace.GetTypeMember("C"); + Assert.Equal(1, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + var cM01 = (MethodSymbol)c.FindImplementationForInterfaceMember(m01); Assert.True(cM01.IsStatic); @@ -11392,6 +11411,7 @@ void validate(ModuleSymbol module) Assert.False(cM01.IsMetadataVirtual()); Assert.False(cM01.IsMetadataFinal); Assert.False(cM01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.Ordinary, cM01.MethodKind); Assert.Equal("void C.M01()", cM01.ToTestDisplayString()); @@ -11442,6 +11462,8 @@ void validate(ModuleSymbol module) var m01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); var c = module.GlobalNamespace.GetTypeMember("C"); + Assert.Equal(1, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + var cM01 = (MethodSymbol)c.FindImplementationForInterfaceMember(m01); Assert.True(cM01.IsStatic); @@ -11450,6 +11472,7 @@ void validate(ModuleSymbol module) Assert.False(cM01.IsMetadataVirtual()); Assert.False(cM01.IsMetadataFinal); Assert.False(cM01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, cM01.MethodKind); Assert.Equal("void C.I1.M01()", cM01.ToTestDisplayString()); Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); @@ -11508,6 +11531,7 @@ public class C3 : C2, I1 void validate(ModuleSymbol module) { var c3 = module.GlobalNamespace.GetTypeMember("C3"); + Assert.Empty(c3.GetMembers().OfType().Where(m => !m.IsConstructor())); var m01 = c3.Interfaces().Single().GetMembers().OfType().Single(); var cM01 = (MethodSymbol)c3.FindImplementationForInterfaceMember(m01); @@ -11776,6 +11800,7 @@ void validate(ModuleSymbol module) if (module is PEModuleSymbol) { + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c2M01.MethodKind); Assert.Equal("void C2.I1.M01()", c2M01.ToTestDisplayString()); Assert.Same(m01, c2M01.ExplicitInterfaceImplementations.Single()); @@ -11787,9 +11812,11 @@ void validate(ModuleSymbol module) Assert.False(c1M01.IsMetadataVirtual()); Assert.False(c1M01.IsMetadataFinal); Assert.False(c1M01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.Ordinary, c1M01.MethodKind); } else { + Assert.Equal(MethodKind.Ordinary, c2M01.MethodKind); Assert.Equal("void C1.M01()", c2M01.ToTestDisplayString()); Assert.Empty(c2M01.ExplicitInterfaceImplementations); } @@ -11827,6 +11854,11 @@ class C1 : I1 { public static void M01() {} } + +class C2 : I1 +{ + static void I1.M01() {} +} "; var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, @@ -11851,6 +11883,7 @@ void validate(ModuleSymbol module) if (module is PEModuleSymbol) { + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c1M01.MethodKind); Assert.Equal("void modopt(I1) C1.I1.M01()", c1M01.ToTestDisplayString()); Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); @@ -11863,14 +11896,33 @@ void validate(ModuleSymbol module) Assert.False(c1M01.IsMetadataVirtual()); Assert.False(c1M01.IsMetadataFinal); Assert.False(c1M01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.Ordinary, c1M01.MethodKind); Assert.Empty(c1M01.ExplicitInterfaceImplementations); } else { + Assert.Equal(MethodKind.Ordinary, c1M01.MethodKind); Assert.Equal("void C1.M01()", c1M01.ToTestDisplayString()); Assert.Empty(c1M01.ExplicitInterfaceImplementations); } + + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + + var c2M01 = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01); + + Assert.True(c2M01.IsStatic); + Assert.False(c2M01.IsAbstract); + Assert.False(c2M01.IsVirtual); + Assert.False(c2M01.IsMetadataVirtual()); + Assert.False(c2M01.IsMetadataFinal); + Assert.False(c2M01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c2M01.MethodKind); + + Assert.Equal("void modopt(I1) C2.I1.M01()", c2M01.ToTestDisplayString()); + Assert.Same(m01, c2M01.ExplicitInterfaceImplementations.Single()); + + Assert.Same(c2M01, c2.GetMembers().OfType().Where(m => !m.IsConstructor()).Single()); } verifier.VerifyIL("C1.I1.M01()", @@ -12485,8 +12537,6 @@ public class C11 : C1, I1 } "; - - var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, parseOptions: TestOptions.RegularPreview, targetFramework: TargetFramework.NetCoreApp, @@ -12542,5 +12592,3464 @@ void validate(ModuleSymbol module) } } } + + private static string UnaryOperatorName(string op) => OperatorFacts.UnaryOperatorNameFromSyntaxKindIfAny(SyntaxFactory.ParseToken(op).Kind()); + private static string BinaryOperatorName(string op) => op switch { ">>" => WellKnownMemberNames.RightShiftOperatorName, _ => OperatorFacts.BinaryOperatorNameFromSyntaxKindIfAny(SyntaxFactory.ParseToken(op).Kind()) }; + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticUnaryOperator_01([CombinatorialValues("+", "-", "!", "~", "++", "--", "true", "false")] string op, bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + string opName = UnaryOperatorName(op); + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static T operator " + op + @"(T x); +} + +" + typeKeyword + @" + C1 : I1 +{} + +" + typeKeyword + @" + C2 : I1 +{ + public C2 operator " + op + @"(C2 x) => throw null; +} + +" + typeKeyword + @" + C3 : I1 +{ + static C3 operator " + op + @"(C3 x) => throw null; +} + +" + typeKeyword + @" + C4 : I1 +{ + C4 I1.operator " + op + @"(C4 x) => throw null; +} + +" + typeKeyword + @" + C5 : I1 +{ + public static int operator " + op + @" (C5 x) => throw null; +} + +" + typeKeyword + @" + C6 : I1 +{ + static int I1.operator " + op + @" (C6 x) => throw null; +} + +" + typeKeyword + @" + C7 : I1 +{ + public static C7 " + opName + @"(C7 x) => throw null; +} + +" + typeKeyword + @" + C8 : I1 +{ + static C8 I1." + opName + @"(C8 x) => throw null; +} + +public interface I2 where T : I2 +{ + abstract static T " + opName + @"(T x); +} + +" + typeKeyword + @" + C9 : I2 +{ + public static C9 operator " + op + @"(C9 x) => throw null; +} + +" + typeKeyword + @" + C10 : I2 +{ + static C10 I2.operator " + op + @"(C10 x) => throw null; +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_BadIncDecRetType or (int)ErrorCode.ERR_OperatorNeedsMatch or (int)ErrorCode.ERR_OpTFRetType)).Verify( + // (8,10): error CS0535: 'C1' does not implement interface member 'I1.operator +(C1)' + // C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.operator " + op + "(C1)").WithLocation(8, 10), + // (12,10): error CS9109: 'C2' does not implement static interface member 'I1.operator +(C2)'. 'C2.operator +(C2)' cannot implement the interface member because it is not static. + // C2 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotStatic, "I1").WithArguments("C2", "I1.operator " + op + "(C2)", "C2.operator " + op + "(C2)").WithLocation(12, 10), + // (14,24): error CS0558: User-defined operator 'C2.operator +(C2)' must be declared static and public + // public C2 operator +(C2 x) => throw null; + Diagnostic(ErrorCode.ERR_OperatorsMustBeStatic, op).WithArguments("C2.operator " + op + "(C2)").WithLocation(14, 24), + // (18,10): error CS0737: 'C3' does not implement interface member 'I1.operator +(C3)'. 'C3.operator +(C3)' cannot implement an interface member because it is not public. + // C3 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotPublic, "I1").WithArguments("C3", "I1.operator " + op + "(C3)", "C3.operator " + op + "(C3)").WithLocation(18, 10), + // (20,24): error CS0558: User-defined operator 'C3.operator +(C3)' must be declared static and public + // static C3 operator +(C3 x) => throw null; + Diagnostic(ErrorCode.ERR_OperatorsMustBeStatic, op).WithArguments("C3.operator " + op + "(C3)").WithLocation(20, 24), + // (24,10): error CS0535: 'C4' does not implement interface member 'I1.operator +(C4)' + // C4 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C4", "I1.operator " + op + "(C4)").WithLocation(24, 10), + // (26,24): error CS9111: Explicit implementation of a user-defined operator 'C4.operator +(C4)' must be declared static + // C4 I1.operator +(C4 x) => throw null; + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, op).WithArguments("C4.operator " + op + "(C4)").WithLocation(26, 24), + // (26,24): error CS0539: 'C4.operator +(C4)' in explicit interface declaration is not found among members of the interface that can be implemented + // C4 I1.operator +(C4 x) => throw null; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("C4.operator " + op + "(C4)").WithLocation(26, 24), + // (30,10): error CS0738: 'C5' does not implement interface member 'I1.operator +(C5)'. 'C5.operator +(C5)' cannot implement 'I1.operator +(C5)' because it does not have the matching return type of 'C5'. + // C5 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberWrongReturnType, "I1").WithArguments("C5", "I1.operator " + op + "(C5)", "C5.operator " + op + "(C5)", "C5").WithLocation(30, 10), + // (36,10): error CS0535: 'C6' does not implement interface member 'I1.operator +(C6)' + // C6 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C6", "I1.operator " + op + "(C6)").WithLocation(36, 10), + // (38,32): error CS0539: 'C6.operator +(C6)' in explicit interface declaration is not found among members of the interface that can be implemented + // static int I1.operator + (C6 x) => throw null; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("C6.operator " + op + "(C6)").WithLocation(38, 32), + // (42,10): error CS0535: 'C7' does not implement interface member 'I1.operator +(C7)' + // C7 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C7", "I1.operator " + op + "(C7)").WithLocation(42, 10), + // (48,10): error CS0535: 'C8' does not implement interface member 'I1.operator +(C8)' + // C8 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C8", "I1.operator " + op + "(C8)").WithLocation(48, 10), + // (50,22): error CS0539: 'C8.op_UnaryPlus(C8)' in explicit interface declaration is not found among members of the interface that can be implemented + // static C8 I1.op_UnaryPlus(C8 x) => throw null; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, opName).WithArguments("C8." + opName + "(C8)").WithLocation(50, 22), + // (59,10): error CS0535: 'C9' does not implement interface member 'I2.op_UnaryPlus(C9)' + // C9 : I2 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I2").WithArguments("C9", "I2." + opName + "(C9)").WithLocation(59, 10), + // (65,11): error CS0535: 'C10' does not implement interface member 'I2.op_UnaryPlus(C10)' + // C10 : I2 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I2").WithArguments("C10", "I2." + opName + "(C10)").WithLocation(65, 11), + // (67,33): error CS0539: 'C10.operator +(C10)' in explicit interface declaration is not found among members of the interface that can be implemented + // static C10 I2.operator +(C10 x) => throw null; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("C10.operator " + op + "(C10)").WithLocation(67, 33) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticBinaryOperator_01([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op, bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + string opName = BinaryOperatorName(op); + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static T operator " + op + @"(T x, int y); +} + +" + typeKeyword + @" + C1 : I1 +{} + +" + typeKeyword + @" + C2 : I1 +{ + public C2 operator " + op + @"(C2 x, int y) => throw null; +} + +" + typeKeyword + @" + C3 : I1 +{ + static C3 operator " + op + @"(C3 x, int y) => throw null; +} + +" + typeKeyword + @" + C4 : I1 +{ + C4 I1.operator " + op + @"(C4 x, int y) => throw null; +} + +" + typeKeyword + @" + C5 : I1 +{ + public static int operator " + op + @" (C5 x, int y) => throw null; +} + +" + typeKeyword + @" + C6 : I1 +{ + static int I1.operator " + op + @" (C6 x, int y) => throw null; +} + +" + typeKeyword + @" + C7 : I1 +{ + public static C7 " + opName + @"(C7 x, int y) => throw null; +} + +" + typeKeyword + @" + C8 : I1 +{ + static C8 I1." + opName + @"(C8 x, int y) => throw null; +} + +public interface I2 where T : I2 +{ + abstract static T " + opName + @"(T x, int y); +} + +" + typeKeyword + @" + C9 : I2 +{ + public static C9 operator " + op + @"(C9 x, int y) => throw null; +} + +" + typeKeyword + @" + C10 : I2 +{ + static C10 I2.operator " + op + @"(C10 x, int y) => throw null; +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + // (8,10): error CS0535: 'C1' does not implement interface member 'I1.operator >>(C1, int)' + // C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.operator " + op + "(C1, int)").WithLocation(8, 10), + // (12,10): error CS9109: 'C2' does not implement static interface member 'I1.operator >>(C2, int)'. 'C2.operator >>(C2, int)' cannot implement the interface member because it is not static. + // C2 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotStatic, "I1").WithArguments("C2", "I1.operator " + op + "(C2, int)", "C2.operator " + op + "(C2, int)").WithLocation(12, 10), + // (14,24): error CS0558: User-defined operator 'C2.operator >>(C2, int)' must be declared static and public + // public C2 operator >>(C2 x, int y) => throw null; + Diagnostic(ErrorCode.ERR_OperatorsMustBeStatic, op).WithArguments("C2.operator " + op + "(C2, int)").WithLocation(14, 24), + // (18,10): error CS0737: 'C3' does not implement interface member 'I1.operator >>(C3, int)'. 'C3.operator >>(C3, int)' cannot implement an interface member because it is not public. + // C3 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotPublic, "I1").WithArguments("C3", "I1.operator " + op + "(C3, int)", "C3.operator " + op + "(C3, int)").WithLocation(18, 10), + // (20,24): error CS0558: User-defined operator 'C3.operator >>(C3, int)' must be declared static and public + // static C3 operator >>(C3 x, int y) => throw null; + Diagnostic(ErrorCode.ERR_OperatorsMustBeStatic, op).WithArguments("C3.operator " + op + "(C3, int)").WithLocation(20, 24), + // (24,10): error CS0535: 'C4' does not implement interface member 'I1.operator >>(C4, int)' + // C4 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C4", "I1.operator " + op + "(C4, int)").WithLocation(24, 10), + // (26,24): error CS9111: Explicit implementation of a user-defined operator 'C4.operator >>(C4, int)' must be declared static + // C4 I1.operator >>(C4 x, int y) => throw null; + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, op).WithArguments("C4.operator " + op + "(C4, int)").WithLocation(26, 24), + // (26,24): error CS0539: 'C4.operator >>(C4, int)' in explicit interface declaration is not found among members of the interface that can be implemented + // C4 I1.operator >>(C4 x, int y) => throw null; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("C4.operator " + op + "(C4, int)").WithLocation(26, 24), + // (30,10): error CS0738: 'C5' does not implement interface member 'I1.operator >>(C5, int)'. 'C5.operator >>(C5, int)' cannot implement 'I1.operator >>(C5, int)' because it does not have the matching return type of 'C5'. + // C5 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberWrongReturnType, "I1").WithArguments("C5", "I1.operator " + op + "(C5, int)", "C5.operator " + op + "(C5, int)", "C5").WithLocation(30, 10), + // (36,10): error CS0535: 'C6' does not implement interface member 'I1.operator >>(C6, int)' + // C6 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C6", "I1.operator " + op + "(C6, int)").WithLocation(36, 10), + // (38,32): error CS0539: 'C6.operator >>(C6, int)' in explicit interface declaration is not found among members of the interface that can be implemented + // static int I1.operator >> (C6 x, int y) => throw null; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("C6.operator " + op + "(C6, int)").WithLocation(38, 32), + // (42,10): error CS0535: 'C7' does not implement interface member 'I1.operator >>(C7, int)' + // C7 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C7", "I1.operator " + op + "(C7, int)").WithLocation(42, 10), + // (48,10): error CS0535: 'C8' does not implement interface member 'I1.operator >>(C8, int)' + // C8 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C8", "I1.operator " + op + "(C8, int)").WithLocation(48, 10), + // (50,22): error CS0539: 'C8.op_RightShift(C8, int)' in explicit interface declaration is not found among members of the interface that can be implemented + // static C8 I1.op_RightShift(C8 x, int y) => throw null; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, opName).WithArguments("C8." + opName + "(C8, int)").WithLocation(50, 22), + // (59,10): error CS0535: 'C9' does not implement interface member 'I2.op_RightShift(C9, int)' + // C9 : I2 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I2").WithArguments("C9", "I2." + opName + "(C9, int)").WithLocation(59, 10), + // (65,11): error CS0535: 'C10' does not implement interface member 'I2.op_RightShift(C10, int)' + // C10 : I2 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I2").WithArguments("C10", "I2." + opName + "(C10, int)").WithLocation(65, 11), + // (67,33): error CS0539: 'C10.operator >>(C10, int)' in explicit interface declaration is not found among members of the interface that can be implemented + // static C10 I2.operator >>(C10 x, int y) => throw null; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("C10.operator " + op + "(C10, int)").WithLocation(67, 33) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticUnaryOperator_03([CombinatorialValues("+", "-", "!", "~", "++", "--", "true", "false")] string op) + { + var source1 = +@" +public interface I1 +{ + abstract static I1 operator " + op + @"(I1 x); +} + +interface I2 : I1 +{} + +interface I3 : I1 +{ + I1 operator " + op + @"(I1 x) => default; +} + +interface I4 : I1 +{ + static I1 operator " + op + @"(I1 x) => default; +} + +interface I5 : I1 +{ + I1 I1.operator " + op + @"(I1 x) => default; +} + +interface I6 : I1 +{ + static I1 I1.operator " + op + @"(I1 x) => default; +} + +interface I7 : I1 +{ + abstract static I1 operator " + op + @"(I1 x); +} + +public interface I11 where T : I11 +{ + abstract static T operator " + op + @"(T x); +} + +interface I8 : I11 where T : I8 +{ + T operator " + op + @"(T x) => default; +} + +interface I9 : I11 where T : I9 +{ + static T operator " + op + @"(T x) => default; +} + +interface I10 : I11 where T : I10 +{ + abstract static T operator " + op + @"(T x); +} + +interface I12 : I11 where T : I12 +{ + static T I11.operator " + op + @"(T x) => default; +} + +interface I13 : I11 where T : I13 +{ + abstract static T I11.operator " + op + @"(T x); +} + +interface I14 : I1 +{ + abstract static I1 I1.operator " + op + @"(I1 x); +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + ErrorCode badSignatureError = op.Length != 2 ? ErrorCode.ERR_BadUnaryOperatorSignature : ErrorCode.ERR_BadIncDecSignature; + ErrorCode badAbstractSignatureError = op.Length != 2 ? ErrorCode.ERR_BadAbstractUnaryOperatorSignature : ErrorCode.ERR_BadAbstractIncDecSignature; + + compilation1.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_OperatorNeedsMatch or (int)ErrorCode.ERR_OpTFRetType)).Verify( + // (12,17): error CS0558: User-defined operator 'I3.operator +(I1)' must be declared static and public + // I1 operator +(I1 x) => default; + Diagnostic(ErrorCode.ERR_OperatorsMustBeStatic, op).WithArguments("I3.operator " + op + "(I1)").WithLocation(12, 17), + // (12,17): error CS0562: The parameter of a unary operator must be the containing type + // I1 operator +(I1 x) => default; + Diagnostic(badSignatureError, op).WithLocation(12, 17), + // (17,24): error CS0562: The parameter of a unary operator must be the containing type + // static I1 operator +(I1 x) => default; + Diagnostic(badSignatureError, op).WithLocation(17, 24), + // (22,20): error CS9111: Explicit implementation of a user-defined operator 'I5.operator +(I1)' must be declared static + // I1 I1.operator +(I1 x) => default; + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, op).WithArguments("I5.operator " + op + "(I1)").WithLocation(22, 20), + // (22,20): error CS0539: 'I5.operator +(I1)' in explicit interface declaration is not found among members of the interface that can be implemented + // I1 I1.operator +(I1 x) => default; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("I5.operator " + op + "(I1)").WithLocation(22, 20), + // (27,27): error CS0539: 'I6.operator +(I1)' in explicit interface declaration is not found among members of the interface that can be implemented + // static I1 I1.operator +(I1 x) => default; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("I6.operator " + op + "(I1)").WithLocation(27, 27), + // (32,33): error CS9102: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + // abstract static I1 operator +(I1 x); + Diagnostic(badAbstractSignatureError, op).WithLocation(32, 33), + // (42,16): error CS0558: User-defined operator 'I8.operator +(T)' must be declared static and public + // T operator +(T x) => default; + Diagnostic(ErrorCode.ERR_OperatorsMustBeStatic, op).WithArguments("I8.operator " + op + "(T)").WithLocation(42, 16), + // (42,16): error CS0562: The parameter of a unary operator must be the containing type + // T operator +(T x) => default; + Diagnostic(badSignatureError, op).WithLocation(42, 16), + // (47,23): error CS0562: The parameter of a unary operator must be the containing type + // static T operator +(T x) => default; + Diagnostic(badSignatureError, op).WithLocation(47, 23), + // (57,30): error CS0539: 'I12.operator +(T)' in explicit interface declaration is not found among members of the interface that can be implemented + // static T I11.operator +(T x) => default; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("I12.operator " + op + "(T)").WithLocation(57, 30), + // (62,39): error CS0106: The modifier 'abstract' is not valid for this item + // abstract static T I11.operator +(T x); + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(62, 39), + // (62,39): error CS0501: 'I13.operator +(T)' must declare a body because it is not marked abstract, extern, or partial + // abstract static T I11.operator +(T x); + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, op).WithArguments("I13.operator " + op + "(T)").WithLocation(62, 39), + // (62,39): error CS0539: 'I13.operator +(T)' in explicit interface declaration is not found among members of the interface that can be implemented + // abstract static T I11.operator +(T x); + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("I13.operator " + op + "(T)").WithLocation(62, 39), + // (67,36): error CS0106: The modifier 'abstract' is not valid for this item + // abstract static I1 I1.operator +(I1 x); + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(67, 36), + // (67,36): error CS0501: 'I14.operator +(I1)' must declare a body because it is not marked abstract, extern, or partial + // abstract static I1 I1.operator +(I1 x); + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, op).WithArguments("I14.operator " + op + "(I1)").WithLocation(67, 36), + // (67,36): error CS0539: 'I14.operator +(I1)' in explicit interface declaration is not found among members of the interface that can be implemented + // abstract static I1 I1.operator +(I1 x); + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("I14.operator " + op + "(I1)").WithLocation(67, 36) + ); + + var m01 = compilation1.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); + + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I2").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I3").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I4").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I5").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I6").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I7").FindImplementationForInterfaceMember(m01)); + + var i8 = compilation1.GlobalNamespace.GetTypeMember("I8"); + Assert.Null(i8.FindImplementationForInterfaceMember(i8.Interfaces().Single().GetMembers().OfType().Single())); + + var i9 = compilation1.GlobalNamespace.GetTypeMember("I9"); + Assert.Null(i9.FindImplementationForInterfaceMember(i9.Interfaces().Single().GetMembers().OfType().Single())); + + var i10 = compilation1.GlobalNamespace.GetTypeMember("I10"); + Assert.Null(i10.FindImplementationForInterfaceMember(i10.Interfaces().Single().GetMembers().OfType().Single())); + + var i12 = compilation1.GlobalNamespace.GetTypeMember("I12"); + Assert.Null(i12.FindImplementationForInterfaceMember(i12.Interfaces().Single().GetMembers().OfType().Single())); + + var i13 = compilation1.GlobalNamespace.GetTypeMember("I13"); + Assert.Null(i13.FindImplementationForInterfaceMember(i13.Interfaces().Single().GetMembers().OfType().Single())); + + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I14").FindImplementationForInterfaceMember(m01)); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticBinaryOperator_03([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op) + { + var source1 = +@" +public interface I1 +{ + abstract static I1 operator " + op + @"(I1 x, int y); +} + +interface I2 : I1 +{} + +interface I3 : I1 +{ + I1 operator " + op + @"(I1 x, int y) => default; +} + +interface I4 : I1 +{ + static I1 operator " + op + @"(I1 x, int y) => default; +} + +interface I5 : I1 +{ + I1 I1.operator " + op + @"(I1 x, int y) => default; +} + +interface I6 : I1 +{ + static I1 I1.operator " + op + @"(I1 x, int y) => default; +} + +interface I7 : I1 +{ + abstract static I1 operator " + op + @"(I1 x, int y); +} + +public interface I11 where T : I11 +{ + abstract static T operator " + op + @"(T x, int y); +} + +interface I8 : I11 where T : I8 +{ + T operator " + op + @"(T x, int y) => default; +} + +interface I9 : I11 where T : I9 +{ + static T operator " + op + @"(T x, int y) => default; +} + +interface I10 : I11 where T : I10 +{ + abstract static T operator " + op + @"(T x, int y); +} + +interface I12 : I11 where T : I12 +{ + static T I11.operator " + op + @"(T x, int y) => default; +} + +interface I13 : I11 where T : I13 +{ + abstract static T I11.operator " + op + @"(T x, int y); +} + +interface I14 : I1 +{ + abstract static I1 I1.operator " + op + @"(I1 x, int y); +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + bool isShift = op == "<<" || op == ">>"; + ErrorCode badSignatureError = isShift ? ErrorCode.ERR_BadShiftOperatorSignature : ErrorCode.ERR_BadBinaryOperatorSignature; + ErrorCode badAbstractSignatureError = isShift ? ErrorCode.ERR_BadAbstractShiftOperatorSignature : ErrorCode.ERR_BadAbstractBinaryOperatorSignature; + + compilation1.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + // (12,17): error CS0558: User-defined operator 'I3.operator |(I1, int)' must be declared static and public + // I1 operator |(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_OperatorsMustBeStatic, op).WithArguments("I3.operator " + op + "(I1, int)").WithLocation(12, 17), + // (12,17): error CS0563: One of the parameters of a binary operator must be the containing type + // I1 operator |(I1 x, int y) => default; + Diagnostic(badSignatureError, op).WithLocation(12, 17), + // (17,24): error CS0563: One of the parameters of a binary operator must be the containing type + // static I1 operator |(I1 x, int y) => default; + Diagnostic(badSignatureError, op).WithLocation(17, 24), + // (22,20): error CS9111: Explicit implementation of a user-defined operator 'I5.operator |(I1, int)' must be declared static + // I1 I1.operator |(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, op).WithArguments("I5.operator " + op + "(I1, int)").WithLocation(22, 20), + // (22,20): error CS0539: 'I5.operator |(I1, int)' in explicit interface declaration is not found among members of the interface that can be implemented + // I1 I1.operator |(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("I5.operator " + op + "(I1, int)").WithLocation(22, 20), + // (27,27): error CS0539: 'I6.operator |(I1, int)' in explicit interface declaration is not found among members of the interface that can be implemented + // static I1 I1.operator |(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("I6.operator " + op + "(I1, int)").WithLocation(27, 27), + // (32,33): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // abstract static I1 operator |(I1 x, int y); + Diagnostic(badAbstractSignatureError, op).WithLocation(32, 33), + // (42,16): error CS0558: User-defined operator 'I8.operator |(T, int)' must be declared static and public + // T operator |(T x, int y) => default; + Diagnostic(ErrorCode.ERR_OperatorsMustBeStatic, op).WithArguments("I8.operator " + op + "(T, int)").WithLocation(42, 16), + // (42,16): error CS0563: One of the parameters of a binary operator must be the containing type + // T operator |(T x, int y) => default; + Diagnostic(badSignatureError, op).WithLocation(42, 16), + // (47,23): error CS0563: One of the parameters of a binary operator must be the containing type + // static T operator |(T x, int y) => default; + Diagnostic(badSignatureError, op).WithLocation(47, 23), + // (57,30): error CS0539: 'I12.operator |(T, int)' in explicit interface declaration is not found among members of the interface that can be implemented + // static T I11.operator |(T x, int y) => default; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("I12.operator " + op + "(T, int)").WithLocation(57, 30), + // (62,39): error CS0106: The modifier 'abstract' is not valid for this item + // abstract static T I11.operator |(T x, int y); + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(62, 39), + // (62,39): error CS0501: 'I13.operator |(T, int)' must declare a body because it is not marked abstract, extern, or partial + // abstract static T I11.operator |(T x, int y); + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, op).WithArguments("I13.operator " + op + "(T, int)").WithLocation(62, 39), + // (62,39): error CS0539: 'I13.operator |(T, int)' in explicit interface declaration is not found among members of the interface that can be implemented + // abstract static T I11.operator |(T x, int y); + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("I13.operator " + op + "(T, int)").WithLocation(62, 39), + // (67,36): error CS0106: The modifier 'abstract' is not valid for this item + // abstract static I1 I1.operator |(I1 x, int y); + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(67, 36), + // (67,36): error CS0501: 'I14.operator |(I1, int)' must declare a body because it is not marked abstract, extern, or partial + // abstract static I1 I1.operator |(I1 x, int y); + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, op).WithArguments("I14.operator " + op + "(I1, int)").WithLocation(67, 36), + // (67,36): error CS0539: 'I14.operator |(I1, int)' in explicit interface declaration is not found among members of the interface that can be implemented + // abstract static I1 I1.operator |(I1 x, int y); + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("I14.operator " + op + "(I1, int)").WithLocation(67, 36) + ); + + var m01 = compilation1.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); + + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I2").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I3").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I4").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I5").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I6").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I7").FindImplementationForInterfaceMember(m01)); + + var i8 = compilation1.GlobalNamespace.GetTypeMember("I8"); + Assert.Null(i8.FindImplementationForInterfaceMember(i8.Interfaces().Single().GetMembers().OfType().Single())); + + var i9 = compilation1.GlobalNamespace.GetTypeMember("I9"); + Assert.Null(i9.FindImplementationForInterfaceMember(i9.Interfaces().Single().GetMembers().OfType().Single())); + + var i10 = compilation1.GlobalNamespace.GetTypeMember("I10"); + Assert.Null(i10.FindImplementationForInterfaceMember(i10.Interfaces().Single().GetMembers().OfType().Single())); + + var i12 = compilation1.GlobalNamespace.GetTypeMember("I12"); + Assert.Null(i12.FindImplementationForInterfaceMember(i12.Interfaces().Single().GetMembers().OfType().Single())); + + var i13 = compilation1.GlobalNamespace.GetTypeMember("I13"); + Assert.Null(i13.FindImplementationForInterfaceMember(i13.Interfaces().Single().GetMembers().OfType().Single())); + + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I14").FindImplementationForInterfaceMember(m01)); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticUnaryOperator_04([CombinatorialValues("+", "-", "!", "~", "++", "--", "true", "false")] string op, bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static I1 operator " + op + @"(I1 x); +} + +public interface I2 where T : I2 +{ + abstract static T operator " + op + @"(T x); +} +"; + var source2 = +typeKeyword + @" + Test1: I1 +{ + static I1 I1.operator " + op + @"(I1 x) => default; +} +" + typeKeyword + @" + Test2: I2 +{ + public static Test2 operator " + op + @"(Test2 x) => default; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_OperatorNeedsMatch or (int)ErrorCode.ERR_OpTFRetType)).Verify( + // (4,15): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // static I1 I1.operator +(I1 x) => default; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "I1.").WithArguments("static abstract members in interfaces").WithLocation(4, 15) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation3.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_OperatorNeedsMatch or (int)ErrorCode.ERR_OpTFRetType)).Verify( + // (4,15): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // static I1 I1.operator +(I1 x) => default; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "I1.").WithArguments("static abstract members in interfaces").WithLocation(4, 15), + // (14,33): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static I1 operator +(I1 x); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, op).WithArguments("abstract", "9.0", "preview").WithLocation(14, 33), + // (19,32): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static T operator +(T x); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, op).WithArguments("abstract", "9.0", "preview").WithLocation(19, 32) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticBinaryOperator_04([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op, bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static I1 operator " + op + @"(I1 x, int y); +} + +public interface I2 where T : I2 +{ + abstract static T operator " + op + @"(T x, int y); +} +"; + var source2 = +typeKeyword + @" + Test1: I1 +{ + static I1 I1.operator " + op + @"(I1 x, int y) => default; +} +" + typeKeyword + @" + Test2: I2 +{ + public static Test2 operator " + op + @"(Test2 x, int y) => default; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + // (4,15): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // static I1 I1.operator +(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "I1.").WithArguments("static abstract members in interfaces").WithLocation(4, 15) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation3.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + // (4,15): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // static I1 I1.operator +(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "I1.").WithArguments("static abstract members in interfaces").WithLocation(4, 15), + // (14,33): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static I1 operator +(I1 x, int y); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, op).WithArguments("abstract", "9.0", "preview").WithLocation(14, 33), + // (19,32): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static T operator +(T x, int y); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, op).WithArguments("abstract", "9.0", "preview").WithLocation(19, 32) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticUnaryOperator_05([CombinatorialValues("+", "-", "!", "~", "++", "--", "true", "false")] string op, bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static T operator " + op + @"(T x); +} +"; + var source2 = +typeKeyword + @" + Test1: I1 +{ + public static Test1 operator " + op + @"(Test1 x) => default; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_OperatorNeedsMatch or (int)ErrorCode.ERR_OpTFRetType)).Verify( + // (2,12): error CS9110: 'Test1.operator +(Test1)' cannot implement interface member 'I1.operator +(Test1)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.operator " + op + "(Test1)", "I1.operator " + op + "(Test1)", "Test1").WithLocation(2, 12) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation3.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_OperatorNeedsMatch or (int)ErrorCode.ERR_OpTFRetType)).Verify( + // (2,12): error CS9110: 'Test1.operator +(Test1)' cannot implement interface member 'I1.operator +(Test1)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.operator " + op + "(Test1)", "I1.operator " + op + "(Test1)", "Test1").WithLocation(2, 12), + // (9,32): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static T operator +(T x); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(9, 32) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticBinaryOperator_05([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op, bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static T operator " + op + @"(T x, int y); +} +"; + var source2 = +typeKeyword + @" + Test1: I1 +{ + public static Test1 operator " + op + @"(Test1 x, int y) => default; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + // (2,12): error CS9110: 'Test1.operator >>(Test1, int)' cannot implement interface member 'I1.operator >>(Test1, int)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.operator " + op + "(Test1, int)", "I1.operator " + op + "(Test1, int)", "Test1").WithLocation(2, 12) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation3.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + // (2,12): error CS9110: 'Test1.operator >>(Test1, int)' cannot implement interface member 'I1.operator >>(Test1, int)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.operator " + op + "(Test1, int)", "I1.operator " + op + "(Test1, int)", "Test1").WithLocation(2, 12), + // (9,32): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static T operator >>(T x, int y); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(9, 32) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticUnaryOperator_06([CombinatorialValues("+", "-", "!", "~", "++", "--", "true", "false")] string op, bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static I1 operator " + op + @"(I1 x); +} +"; + var source2 = +typeKeyword + @" + Test1: I1 +{ + static I1 I1.operator " + op + @"(I1 x) => default; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (4,27): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // static I1 I1.operator +(I1 x) => default; + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(4, 27) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation3.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_OperatorNeedsMatch or (int)ErrorCode.ERR_OpTFRetType)).Verify( + // (4,27): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // static I1 I1.operator +(I1 x) => default; + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(4, 27), + // (9,33): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static I1 operator +(I1 x); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(9, 33) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticBinaryOperator_06([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op, bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static I1 operator " + op + @"(I1 x, int y); +} +"; + var source2 = +typeKeyword + @" + Test1: I1 +{ + static I1 I1.operator " + op + @"(I1 x, int y) => default; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (4,27): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // static I1 I1.operator +(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(4, 27) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation3.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + // (4,27): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // static I1 I1.operator +(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(4, 27), + // (9,33): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static I1 operator +(I1 x, int y); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(9, 33) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticUnaryOperator_07([CombinatorialValues("+", "-", "!", "~", "++", "--")] string op, bool structure) + { + // Basic implicit implementation scenario, MethodImpl is emitted + + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static T operator " + op + @"(T x); +} + +" + typeKeyword + @" + C : I1 +{ + public static C operator " + op + @"(C x) => default; +} +"; + + var opName = UnaryOperatorName(op); + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped, + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false)).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c = module.GlobalNamespace.GetTypeMember("C"); + var i1 = c.Interfaces().Single(); + var m01 = i1.GetMembers().OfType().Single(); + + Assert.Equal(1, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var cM01 = (MethodSymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + Assert.False(cM01.IsMetadataVirtual()); + Assert.False(cM01.IsMetadataFinal); + Assert.False(cM01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.UserDefinedOperator, cM01.MethodKind); + Assert.False(cM01.HasRuntimeSpecialName); + Assert.True(cM01.HasSpecialName); + + Assert.Equal("C C." + opName + "(C x)", cM01.ToTestDisplayString()); + + if (module is PEModuleSymbol) + { + Assert.Equal(m01, cM01.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(cM01.ExplicitInterfaceImplementations); + } + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticUnaryTrueFalseOperator_07([CombinatorialValues("true", "false")] string op, bool structure) + { + // Basic implicit implementation scenario, MethodImpl is emitted + + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public partial interface I1 where T : I1 +{ + abstract static bool operator " + op + @"(T x); +} + +partial " + typeKeyword + @" + C : I1 +{ + public static bool operator " + op + @"(C x) => default; +} +"; + string matchingOp = op == "true" ? "false" : "true"; + + source1 += +@" +public partial interface I1 where T : I1 +{ + abstract static bool operator " + matchingOp + @"(T x); +} + +partial " + typeKeyword + @" + C +{ + public static bool operator " + matchingOp + @"(C x) => default; +} +"; + + var opName = UnaryOperatorName(op); + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped, + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false)).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c = module.GlobalNamespace.GetTypeMember("C"); + var i1 = c.Interfaces().Single(); + var m01 = i1.GetMembers(opName).OfType().Single(); + + Assert.Equal(2, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var cM01 = (MethodSymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + Assert.False(cM01.IsMetadataVirtual()); + Assert.False(cM01.IsMetadataFinal); + Assert.False(cM01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.UserDefinedOperator, cM01.MethodKind); + Assert.False(cM01.HasRuntimeSpecialName); + Assert.True(cM01.HasSpecialName); + + Assert.Equal("System.Boolean C." + opName + "(C x)", cM01.ToTestDisplayString()); + + if (module is PEModuleSymbol) + { + Assert.Equal(m01, cM01.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(cM01.ExplicitInterfaceImplementations); + } + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticBinaryOperator_07([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op, bool structure) + { + // Basic implicit implementation scenario, MethodImpl is emitted + + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public partial interface I1 where T : I1 +{ + abstract static T operator " + op + @"(T x, int y); +} + +partial " + typeKeyword + @" + C : I1 +{ + public static C operator " + op + @"(C x, int y) => default; +} +"; + string matchingOp = MatchingBinaryOperator(op); + + if (matchingOp is object) + { + source1 += +@" +public partial interface I1 where T : I1 +{ + abstract static T operator " + matchingOp + @"(T x, int y); +} + +partial " + typeKeyword + @" + C +{ + public static C operator " + matchingOp + @"(C x, int y) => default; +} +"; + } + + var opName = BinaryOperatorName(op); + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped, + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false)).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c = module.GlobalNamespace.GetTypeMember("C"); + var i1 = c.Interfaces().Single(); + var m01 = i1.GetMembers(opName).OfType().Single(); + + Assert.Equal(matchingOp is null ? 1 : 2, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var cM01 = (MethodSymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + Assert.False(cM01.IsMetadataVirtual()); + Assert.False(cM01.IsMetadataFinal); + Assert.False(cM01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.UserDefinedOperator, cM01.MethodKind); + Assert.False(cM01.HasRuntimeSpecialName); + Assert.True(cM01.HasSpecialName); + + Assert.Equal("C C." + opName + "(C x, System.Int32 y)", cM01.ToTestDisplayString()); + + if (module is PEModuleSymbol) + { + Assert.Equal(m01, cM01.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(cM01.ExplicitInterfaceImplementations); + } + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticUnaryOperator_08([CombinatorialValues("+", "-", "!", "~", "++", "--")] string op, bool structure) + { + // Basic explicit implementation scenario + + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static I1 operator " + op + @"(I1 x); +} + +" + typeKeyword + @" + C : I1 +{ + static I1 I1.operator " + op + @"(I1 x) => default; +} +"; + + var opName = UnaryOperatorName(op); + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var tree = compilation1.SyntaxTrees.Single(); + var model = compilation1.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + + Assert.Equal("default", node.ToString()); + Assert.Equal("I1", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()); + + var declaredSymbol = model.GetDeclaredSymbol(node.FirstAncestorOrSelf()); + Assert.Equal("I1 C.I1." + opName + "(I1 x)", declaredSymbol.ToTestDisplayString()); + Assert.DoesNotContain(opName, declaredSymbol.ContainingType.MemberNames); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped, + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false)).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var m01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); + var c = module.GlobalNamespace.GetTypeMember("C"); + + Assert.Equal(1, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var cM01 = (MethodSymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + Assert.False(cM01.IsMetadataVirtual()); + Assert.False(cM01.IsMetadataFinal); + Assert.False(cM01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, cM01.MethodKind); + Assert.False(cM01.HasRuntimeSpecialName); + Assert.False(cM01.HasSpecialName); + + Assert.Equal("I1 C.I1." + opName + "(I1 x)", cM01.ToTestDisplayString()); + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticUnaryTrueFalseOperator_08([CombinatorialValues("true", "false")] string op, bool structure) + { + // Basic explicit implementation scenario + + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public partial interface I1 +{ + abstract static bool operator " + op + @"(I1 x); +} + +partial " + typeKeyword + @" + C : I1 +{ + static bool I1.operator " + op + @"(I1 x) => default; +} +"; + string matchingOp = op == "true" ? "false" : "true"; + + source1 += +@" +public partial interface I1 +{ + abstract static bool operator " + matchingOp + @"(I1 x); +} + +partial " + typeKeyword + @" + C +{ + static bool I1.operator " + matchingOp + @"(I1 x) => default; +} +"; + + var opName = UnaryOperatorName(op); + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var tree = compilation1.SyntaxTrees.Single(); + var model = compilation1.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().First(); + + Assert.Equal("default", node.ToString()); + Assert.Equal("System.Boolean", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()); + + var declaredSymbol = model.GetDeclaredSymbol(node.FirstAncestorOrSelf()); + Assert.Equal("System.Boolean C.I1." + opName + "(I1 x)", declaredSymbol.ToTestDisplayString()); + Assert.DoesNotContain(opName, declaredSymbol.ContainingType.MemberNames); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped, + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false)).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var m01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers(opName).OfType().Single(); + var c = module.GlobalNamespace.GetTypeMember("C"); + + Assert.Equal(2, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var cM01 = (MethodSymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + Assert.False(cM01.IsMetadataVirtual()); + Assert.False(cM01.IsMetadataFinal); + Assert.False(cM01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, cM01.MethodKind); + Assert.False(cM01.HasRuntimeSpecialName); + Assert.False(cM01.HasSpecialName); + + Assert.Equal("System.Boolean C.I1." + opName + "(I1 x)", cM01.ToTestDisplayString()); + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticBinaryOperator_08([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op, bool structure) + { + // Basic explicit implementation scenario + + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public partial interface I1 +{ + abstract static I1 operator " + op + @"(I1 x, int y); +} + +partial " + typeKeyword + @" + C : I1 +{ + static I1 I1.operator " + op + @"(I1 x, int y) => default; +} +"; + string matchingOp = MatchingBinaryOperator(op); + + if (matchingOp is object) + { + source1 += +@" +public partial interface I1 +{ + abstract static I1 operator " + matchingOp + @"(I1 x, int y); +} + +partial " + typeKeyword + @" + C +{ + static I1 I1.operator " + matchingOp + @"(I1 x, int y) => default; +} +"; + } + + var opName = BinaryOperatorName(op); + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + + var tree = compilation1.SyntaxTrees.Single(); + var model = compilation1.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().First(); + + Assert.Equal("default", node.ToString()); + Assert.Equal("I1", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()); + + var declaredSymbol = model.GetDeclaredSymbol(node.FirstAncestorOrSelf()); + Assert.Equal("I1 C.I1." + opName + "(I1 x, System.Int32 y)", declaredSymbol.ToTestDisplayString()); + Assert.DoesNotContain(opName, declaredSymbol.ContainingType.MemberNames); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped, + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false)).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var m01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers(opName).OfType().Single(); + var c = module.GlobalNamespace.GetTypeMember("C"); + + Assert.Equal(matchingOp is null ? 1 : 2, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var cM01 = (MethodSymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + Assert.False(cM01.IsMetadataVirtual()); + Assert.False(cM01.IsMetadataFinal); + Assert.False(cM01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, cM01.MethodKind); + Assert.False(cM01.HasRuntimeSpecialName); + Assert.False(cM01.HasSpecialName); + + Assert.Equal("I1 C.I1." + opName + "(I1 x, System.Int32 y)", cM01.ToTestDisplayString()); + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticUnaryOperator_09([CombinatorialValues("+", "-", "!", "~", "++", "--")] string op) + { + // Explicit implementation from base is treated as an implementation + + var source1 = +@" +public interface I1 +{ + abstract static I1 operator " + op + @"(I1 x); +} + +public class C2 : I1 +{ + static I1 I1.operator " + op + @"(I1 x) => default; +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C3 : C2, I1 +{ +} +"; + + var opName = UnaryOperatorName(op); + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.EmitToImageReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + Assert.Empty(c3.GetMembers().OfType().Where(m => !m.IsConstructor())); + var m01 = c3.Interfaces().Single().GetMembers().OfType().Single(); + + var cM01 = (MethodSymbol)c3.FindImplementationForInterfaceMember(m01); + + Assert.Equal("I1 C2.I1." + opName + "(I1 x)", cM01.ToTestDisplayString()); + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticUnaryTrueFalseOperator_09([CombinatorialValues("true", "false")] string op) + { + // Explicit implementation from base is treated as an implementation + + var source1 = +@" +public partial interface I1 +{ + abstract static bool operator " + op + @"(I1 x); +} + +public partial class C2 : I1 +{ + static bool I1.operator " + op + @"(I1 x) => default; +} +"; + string matchingOp = op == "true" ? "false" : "true"; + + source1 += +@" +public partial interface I1 +{ + abstract static bool operator " + matchingOp + @"(I1 x); +} + +public partial class C2 +{ + static bool I1.operator " + matchingOp + @"(I1 x) => default; +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C3 : C2, I1 +{ +} +"; + + var opName = UnaryOperatorName(op); + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.EmitToImageReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + Assert.Empty(c3.GetMembers().OfType().Where(m => !m.IsConstructor())); + var m01 = c3.Interfaces().Single().GetMembers(opName).OfType().Single(); + + var cM01 = (MethodSymbol)c3.FindImplementationForInterfaceMember(m01); + + Assert.Equal("System.Boolean C2.I1." + opName + "(I1 x)", cM01.ToTestDisplayString()); + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticBinaryOperator_09([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op) + { + // Explicit implementation from base is treated as an implementation + + var source1 = +@" +public partial interface I1 +{ + abstract static I1 operator " + op + @"(I1 x, int y); +} + +public partial class C2 : I1 +{ + static I1 I1.operator " + op + @"(I1 x, int y) => default; +} +"; + string matchingOp = MatchingBinaryOperator(op); + + if (matchingOp is object) + { + source1 += +@" +public partial interface I1 +{ + abstract static I1 operator " + matchingOp + @"(I1 x, int y); +} + +public partial class C2 +{ + static I1 I1.operator " + matchingOp + @"(I1 x, int y) => default; +} +"; + } + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C3 : C2, I1 +{ +} +"; + + var opName = BinaryOperatorName(op); + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.EmitToImageReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + Assert.Empty(c3.GetMembers().OfType().Where(m => !m.IsConstructor())); + var m01 = c3.Interfaces().Single().GetMembers(opName).OfType().Single(); + + var cM01 = (MethodSymbol)c3.FindImplementationForInterfaceMember(m01); + + Assert.Equal("I1 C2.I1." + opName + "(I1 x, System.Int32 y)", cM01.ToTestDisplayString()); + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticUnaryOperator_10([CombinatorialValues("+", "-", "!", "~", "++", "--", "true", "false")] string op) + { + // Implicit implementation is considered only for types implementing interface in source. + // In metadata, only explicit implementations are considered + + var opName = UnaryOperatorName(op); + + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig specialname abstract virtual static + class I1 " + opName + @" ( + class I1 x + ) cil managed + { + } +} + +.class public auto ansi beforefieldinit C1 + extends System.Object + implements I1 +{ + .method private hidebysig + static class I1 I1." + opName + @" (class I1 x) cil managed + { + .override method class I1 I1::" + opName + @"(class I1) + + IL_0000: ldnull + IL_0001: ret + } + + .method public hidebysig static + specialname class I1 " + opName + @" (class I1 x) cil managed + { + IL_0000: ldnull + IL_0001: ret + } + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + IL_0000: ldarg.0 + IL_0001: call instance void System.Object::.ctor() + IL_0006: ret + } +} + +.class public auto ansi beforefieldinit C2 + extends C1 + implements I1 +{ + .method public hidebysig static + specialname class I1 " + opName + @" (class I1 x) cil managed + { + IL_0000: ldnull + IL_0001: ret + } + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + IL_0000: ldarg.0 + IL_0001: call instance void C1::.ctor() + IL_0006: ret + } // end of method C2::.ctor +} // end of class C2 +"; + var source1 = +@" +public class C3 : C2 +{ +} + +public class C4 : C1, I1 +{ +} + +public class C5 : C2, I1 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); + + Assert.Equal(MethodKind.UserDefinedOperator, m01.MethodKind); + Assert.Equal(MethodKind.UserDefinedOperator, c1.GetMember(opName).MethodKind); + + var c1M01 = (MethodSymbol)c1.FindImplementationForInterfaceMember(m01); + + Assert.Equal("I1 C1.I1." + opName + "(I1 x)", c1M01.ToTestDisplayString()); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c1M01.MethodKind); + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + + var c2 = compilation1.GlobalNamespace.GetTypeMember("C2"); + Assert.Same(c1M01, c2.FindImplementationForInterfaceMember(m01)); + + var c3 = compilation1.GlobalNamespace.GetTypeMember("C3"); + Assert.Same(c1M01, c3.FindImplementationForInterfaceMember(m01)); + + var c4 = compilation1.GlobalNamespace.GetTypeMember("C4"); + Assert.Same(c1M01, c4.FindImplementationForInterfaceMember(m01)); + + var c5 = compilation1.GlobalNamespace.GetTypeMember("C5"); + + var c2M01 = (MethodSymbol)c5.FindImplementationForInterfaceMember(m01); + Assert.Equal("I1 C2." + opName + "(I1 x)", c2M01.ToTestDisplayString()); + Assert.Equal(MethodKind.UserDefinedOperator, c2M01.MethodKind); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticBinaryOperator_10([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op) + { + // Implicit implementation is considered only for types implementing interface in source. + // In metadata, only explicit implementations are considered + + var opName = BinaryOperatorName(op); + + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig specialname abstract virtual static + class I1 " + opName + @" ( + class I1 x, + int32 y + ) cil managed + { + } +} + +.class public auto ansi beforefieldinit C1 + extends System.Object + implements I1 +{ + .method private hidebysig + static class I1 I1." + opName + @" (class I1 x, int32 y) cil managed + { + .override method class I1 I1::" + opName + @"(class I1, int32) + + IL_0000: ldnull + IL_0001: ret + } + + .method public hidebysig static + specialname class I1 " + opName + @" (class I1 x, int32 y) cil managed + { + IL_0000: ldnull + IL_0001: ret + } + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + IL_0000: ldarg.0 + IL_0001: call instance void System.Object::.ctor() + IL_0006: ret + } +} + +.class public auto ansi beforefieldinit C2 + extends C1 + implements I1 +{ + .method public hidebysig static + specialname class I1 " + opName + @" (class I1 x, int32 y) cil managed + { + IL_0000: ldnull + IL_0001: ret + } + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + IL_0000: ldarg.0 + IL_0001: call instance void C1::.ctor() + IL_0006: ret + } // end of method C2::.ctor +} // end of class C2 +"; + var source1 = +@" +public class C3 : C2 +{ +} + +public class C4 : C1, I1 +{ +} + +public class C5 : C2, I1 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); + + Assert.Equal(MethodKind.UserDefinedOperator, m01.MethodKind); + Assert.Equal(MethodKind.UserDefinedOperator, c1.GetMember(opName).MethodKind); + + var c1M01 = (MethodSymbol)c1.FindImplementationForInterfaceMember(m01); + + Assert.Equal("I1 C1.I1." + opName + "(I1 x, System.Int32 y)", c1M01.ToTestDisplayString()); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c1M01.MethodKind); + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + + var c2 = compilation1.GlobalNamespace.GetTypeMember("C2"); + Assert.Same(c1M01, c2.FindImplementationForInterfaceMember(m01)); + + var c3 = compilation1.GlobalNamespace.GetTypeMember("C3"); + Assert.Same(c1M01, c3.FindImplementationForInterfaceMember(m01)); + + var c4 = compilation1.GlobalNamespace.GetTypeMember("C4"); + Assert.Same(c1M01, c4.FindImplementationForInterfaceMember(m01)); + + var c5 = compilation1.GlobalNamespace.GetTypeMember("C5"); + + var c2M01 = (MethodSymbol)c5.FindImplementationForInterfaceMember(m01); + Assert.Equal("I1 C2." + opName + "(I1 x, System.Int32 y)", c2M01.ToTestDisplayString()); + Assert.Equal(MethodKind.UserDefinedOperator, c2M01.MethodKind); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticUnaryOperator_11([CombinatorialValues("+", "-", "!", "~", "++", "--", "true", "false")] string op) + { + // Ignore invalid metadata (non-abstract static virtual method). + + var opName = UnaryOperatorName(op); + + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig specialname virtual + static class I1 " + opName + @" ( + class I1 x + ) cil managed + { + IL_0000: ldnull + IL_0001: ret + } +} +"; + + var source1 = +@" +public class C1 : I1 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyEmitDiagnostics(); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var i1 = c1.Interfaces().Single(); + var m01 = i1.GetMembers().OfType().Single(); + + Assert.Equal(MethodKind.UserDefinedOperator, m01.MethodKind); + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + var source2 = +@" +public class C1 : I1 +{ + static I1 I1.operator " + op + @"(I1 x) => default; +} +"; + + var compilation2 = CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation2.VerifyEmitDiagnostics( + // (4,27): error CS0539: 'C1.operator ~(I1)' in explicit interface declaration is not found among members of the interface that can be implemented + // static I1 I1.operator ~(I1 x) => default; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("C1.operator " + op + "(I1)").WithLocation(4, 27) + ); + + c1 = compilation2.GlobalNamespace.GetTypeMember("C1"); + m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); + + Assert.Equal("I1 I1." + opName + "(I1 x)", m01.ToTestDisplayString()); + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticBinaryOperator_11([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op) + { + // Ignore invalid metadata (non-abstract static virtual method). + + var opName = BinaryOperatorName(op); + + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig specialname virtual + static class I1 " + opName + @" ( + class I1 x, + int32 y + ) cil managed + { + IL_0000: ldnull + IL_0001: ret + } +} +"; + + var source1 = +@" +public class C1 : I1 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyEmitDiagnostics(); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var i1 = c1.Interfaces().Single(); + var m01 = i1.GetMembers().OfType().Single(); + + Assert.Equal(MethodKind.UserDefinedOperator, m01.MethodKind); + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + var source2 = +@" +public class C1 : I1 +{ + static I1 I1.operator " + op + @"(I1 x, int y) => default; +} +"; + + var compilation2 = CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation2.VerifyEmitDiagnostics( + // (4,27): error CS0539: 'C1.operator <(I1, int)' in explicit interface declaration is not found among members of the interface that can be implemented + // static I1 I1.operator <(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("C1.operator " + op + "(I1, int)").WithLocation(4, 27) + ); + + c1 = compilation2.GlobalNamespace.GetTypeMember("C1"); + m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); + + Assert.Equal("I1 I1." + opName + "(I1 x, System.Int32 y)", m01.ToTestDisplayString()); + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticUnaryOperator_12([CombinatorialValues("+", "-", "!", "~", "++", "--", "true", "false")] string op) + { + // Ignore invalid metadata (default interface implementation for a static method) + + var opName = UnaryOperatorName(op); + + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig specialname abstract virtual static + class I1 " + opName + @" ( + class I1 x + ) cil managed + { + } +} +.class interface public auto ansi abstract I2 + implements I1 +{ + .method private hidebysig + static class I1 I1." + opName + @" (class I1 x) cil managed + { + .override method class I1 I1::" + opName + @"(class I1) + + IL_0000: ldnull + IL_0001: ret + } +} +"; + + var source1 = +@" +public class C1 : I2 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyEmitDiagnostics( + // (2,19): error CS0535: 'C1' does not implement interface member 'I1.operator ~(I1)' + // public class C1 : I2 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I2").WithArguments("C1", "I1.operator " + op + "(I1)").WithLocation(2, 19) + ); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var i2 = c1.Interfaces().Single(); + var i1 = i2.Interfaces().Single(); + var m01 = i1.GetMembers().OfType().Single(); + + Assert.Equal(MethodKind.UserDefinedOperator, m01.MethodKind); + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(i2.FindImplementationForInterfaceMember(m01)); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticBinaryOperator_12([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op) + { + // Ignore invalid metadata (default interface implementation for a static method) + + var opName = BinaryOperatorName(op); + + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig specialname abstract virtual static + class I1 " + opName + @" ( + class I1 x, + int32 y + ) cil managed + { + } +} +.class interface public auto ansi abstract I2 + implements I1 +{ + .method private hidebysig + static class I1 I1." + opName + @" (class I1 x, int32 y) cil managed + { + .override method class I1 I1::" + opName + @"(class I1, int32) + + IL_0000: ldnull + IL_0001: ret + } +} +"; + + var source1 = +@" +public class C1 : I2 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyEmitDiagnostics( + // (2,19): error CS0535: 'C1' does not implement interface member 'I1.operator /(I1, int)' + // public class C1 : I2 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I2").WithArguments("C1", "I1.operator " + op + "(I1, int)").WithLocation(2, 19) + ); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var i2 = c1.Interfaces().Single(); + var i1 = i2.Interfaces().Single(); + var m01 = i1.GetMembers().OfType().Single(); + + Assert.Equal(MethodKind.UserDefinedOperator, m01.MethodKind); + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(i2.FindImplementationForInterfaceMember(m01)); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticBinaryOperator_13([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=")] string op) + { + // A forwarding method is added for an implicit implementation declared in base class. + + var source1 = +@" +public partial interface I1 where T : I1 +{ + abstract static T operator " + op + @"(T x, C1 y); +} + +public partial class C1 +{ + public static C2 operator " + op + @"(C2 x, C1 y) => default; +} + +public class C2 : C1, I1 +{ +} +"; + string matchingOp = MatchingBinaryOperator(op); + + if (matchingOp is object) + { + source1 += +@" +public partial interface I1 +{ + abstract static T operator " + matchingOp + @"(T x, C1 y); +} + +public partial class C1 +{ + public static C2 operator " + matchingOp + @"(C2 x, C1 y) => default; +} +"; + } + + var opName = BinaryOperatorName(op); + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + var i1 = c2.Interfaces().Single(); + var m01 = i1.GetMembers(opName).OfType().Single(); + + var c2M01 = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01); + + Assert.True(c2M01.IsStatic); + Assert.False(c2M01.IsAbstract); + Assert.False(c2M01.IsVirtual); + Assert.False(c2M01.IsMetadataVirtual()); + Assert.False(c2M01.IsMetadataFinal); + Assert.False(c2M01.IsMetadataNewSlot()); + + if (module is PEModuleSymbol) + { + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c2M01.MethodKind); + Assert.False(c2M01.HasRuntimeSpecialName); + Assert.False(c2M01.HasSpecialName); + + Assert.Equal("C2 C2.I1." + opName + "(C2 x, C1 y)", c2M01.ToTestDisplayString()); + Assert.Equal(m01, c2M01.ExplicitInterfaceImplementations.Single()); + + var c1M01 = module.GlobalNamespace.GetMember("C1." + opName); + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + Assert.False(c1M01.IsMetadataVirtual()); + Assert.False(c1M01.IsMetadataFinal); + Assert.False(c1M01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.UserDefinedOperator, c1M01.MethodKind); + Assert.False(c1M01.HasRuntimeSpecialName); + Assert.True(c1M01.HasSpecialName); + } + else + { + Assert.Equal(MethodKind.UserDefinedOperator, c2M01.MethodKind); + Assert.False(c2M01.HasRuntimeSpecialName); + Assert.True(c2M01.HasSpecialName); + + Assert.Equal("C2 C1." + opName + "(C2 x, C1 y)", c2M01.ToTestDisplayString()); + Assert.Empty(c2M01.ExplicitInterfaceImplementations); + } + } + + verifier.VerifyIL("C2.I1." + opName + "(C2, C1)", +@" +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: call ""C2 C1." + opName + @"(C2, C1)"" + IL_0007: ret +} +"); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticUnaryOperator_14([CombinatorialValues("+", "-", "!", "~", "++", "--")] string op) + { + // A forwarding method is added for an implicit implementation with modopt mismatch. + + var opName = UnaryOperatorName(op); + + var ilSource = @" +.class interface public auto ansi abstract I1`1<(class I1`1) T> +{ + // Methods + .method public hidebysig specialname abstract virtual static + !T modopt(I1`1) " + opName + @" ( + !T x + ) cil managed + { + } +} +"; + + var source1 = +@" +class C1 : I1 +{ + public static C1 operator " + op + @"(C1 x) => default; +} + +class C2 : I1 +{ + static C2 I1.operator " + op + @"(C2 x) => default; +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c1 = module.GlobalNamespace.GetTypeMember("C1"); + var m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); + + var c1M01 = (MethodSymbol)c1.FindImplementationForInterfaceMember(m01); + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + Assert.False(c1M01.IsMetadataVirtual()); + Assert.False(c1M01.IsMetadataFinal); + Assert.False(c1M01.IsMetadataNewSlot()); + + if (module is PEModuleSymbol) + { + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c1M01.MethodKind); + Assert.Equal("C1 modopt(I1<>) C1.I1." + opName + "(C1 x)", c1M01.ToTestDisplayString()); + Assert.Equal(m01, c1M01.ExplicitInterfaceImplementations.Single()); + + c1M01 = module.GlobalNamespace.GetMember("C1." + opName); + Assert.Equal("C1 C1." + opName + "(C1 x)", c1M01.ToTestDisplayString()); + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + Assert.False(c1M01.IsMetadataVirtual()); + Assert.False(c1M01.IsMetadataFinal); + Assert.False(c1M01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.UserDefinedOperator, c1M01.MethodKind); + + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + } + else + { + Assert.Equal(MethodKind.UserDefinedOperator, c1M01.MethodKind); + Assert.Equal("C1 C1." + opName + "(C1 x)", c1M01.ToTestDisplayString()); + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + } + + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + m01 = c2.Interfaces().Single().GetMembers().OfType().Single(); + var c2M01 = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01); + + Assert.True(c2M01.IsStatic); + Assert.False(c2M01.IsAbstract); + Assert.False(c2M01.IsVirtual); + Assert.False(c2M01.IsMetadataVirtual()); + Assert.False(c2M01.IsMetadataFinal); + Assert.False(c2M01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c2M01.MethodKind); + + Assert.Equal("C2 modopt(I1<>) C2.I1." + opName + "(C2 x)", c2M01.ToTestDisplayString()); + Assert.Equal(m01, c2M01.ExplicitInterfaceImplementations.Single()); + + Assert.Same(c2M01, c2.GetMembers().OfType().Where(m => !m.IsConstructor()).Single()); + } + + verifier.VerifyIL("C1.I1." + opName + "(C1)", +@" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""C1 C1." + opName + @"(C1)"" + IL_0006: ret +} +"); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticUnaryTrueFalseOperator_14([CombinatorialValues("true", "false")] string op) + { + // A forwarding method is added for an implicit implementation with modopt mismatch. + + var opName = UnaryOperatorName(op); + + var ilSource = @" +.class interface public auto ansi abstract I1`1<(class I1`1) T> +{ + // Methods + .method public hidebysig specialname abstract virtual static + bool modopt(I1`1) " + opName + @" ( + !T x + ) cil managed + { + } +} +"; + + var source1 = +@" +class C1 : I1 +{ + public static bool operator " + op + @"(C1 x) => default; + public static bool operator " + (op == "true" ? "false" : "true") + @"(C1 x) => default; +} + +class C2 : I1 +{ + static bool I1.operator " + op + @"(C2 x) => default; +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c1 = module.GlobalNamespace.GetTypeMember("C1"); + var m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); + + var c1M01 = (MethodSymbol)c1.FindImplementationForInterfaceMember(m01); + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + Assert.False(c1M01.IsMetadataVirtual()); + Assert.False(c1M01.IsMetadataFinal); + Assert.False(c1M01.IsMetadataNewSlot()); + + if (module is PEModuleSymbol) + { + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c1M01.MethodKind); + Assert.Equal("System.Boolean modopt(I1<>) C1.I1." + opName + "(C1 x)", c1M01.ToTestDisplayString()); + Assert.Equal(m01, c1M01.ExplicitInterfaceImplementations.Single()); + + c1M01 = module.GlobalNamespace.GetMember("C1." + opName); + Assert.Equal("System.Boolean C1." + opName + "(C1 x)", c1M01.ToTestDisplayString()); + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + Assert.False(c1M01.IsMetadataVirtual()); + Assert.False(c1M01.IsMetadataFinal); + Assert.False(c1M01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.UserDefinedOperator, c1M01.MethodKind); + + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + } + else + { + Assert.Equal(MethodKind.UserDefinedOperator, c1M01.MethodKind); + Assert.Equal("System.Boolean C1." + opName + "(C1 x)", c1M01.ToTestDisplayString()); + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + } + + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + m01 = c2.Interfaces().Single().GetMembers().OfType().Single(); + var c2M01 = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01); + + Assert.True(c2M01.IsStatic); + Assert.False(c2M01.IsAbstract); + Assert.False(c2M01.IsVirtual); + Assert.False(c2M01.IsMetadataVirtual()); + Assert.False(c2M01.IsMetadataFinal); + Assert.False(c2M01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c2M01.MethodKind); + + Assert.Equal("System.Boolean modopt(I1<>) C2.I1." + opName + "(C2 x)", c2M01.ToTestDisplayString()); + Assert.Equal(m01, c2M01.ExplicitInterfaceImplementations.Single()); + + Assert.Same(c2M01, c2.GetMembers().OfType().Where(m => !m.IsConstructor()).Single()); + } + + verifier.VerifyIL("C1.I1." + opName + "(C1)", +@" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""bool C1." + opName + @"(C1)"" + IL_0006: ret +} +"); + } + + private static string MatchingBinaryOperator(string op) + { + return op switch { "<" => ">", ">" => "<", "<=" => ">=", ">=" => "<=", _ => null }; + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticBinaryOperator_14([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op) + { + // A forwarding method is added for an implicit implementation with modopt mismatch. + + var opName = BinaryOperatorName(op); + + var ilSource = @" +.class interface public auto ansi abstract I1`1<(class I1`1) T> +{ + // Methods + .method public hidebysig specialname abstract virtual static + !T modopt(I1`1) " + opName + @" ( + !T x, + int32 y + ) cil managed + { + } +} +"; + string matchingOp = MatchingBinaryOperator(op); + string additionalMethods = ""; + + if (matchingOp is object) + { + additionalMethods = +@" + public static C1 operator " + matchingOp + @"(C1 x, int y) => default; +"; + } + + var source1 = +@" +class C1 : I1 +{ + public static C1 operator " + op + @"(C1 x, int y) => default; +" + additionalMethods + @" +} + +class C2 : I1 +{ + static C2 I1.operator " + op + @"(C2 x, int y) => default; +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c1 = module.GlobalNamespace.GetTypeMember("C1"); + var m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); + + var c1M01 = (MethodSymbol)c1.FindImplementationForInterfaceMember(m01); + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + Assert.False(c1M01.IsMetadataVirtual()); + Assert.False(c1M01.IsMetadataFinal); + Assert.False(c1M01.IsMetadataNewSlot()); + + if (module is PEModuleSymbol) + { + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c1M01.MethodKind); + Assert.Equal("C1 modopt(I1<>) C1.I1." + opName + "(C1 x, System.Int32 y)", c1M01.ToTestDisplayString()); + Assert.Equal(m01, c1M01.ExplicitInterfaceImplementations.Single()); + + c1M01 = module.GlobalNamespace.GetMember("C1." + opName); + Assert.Equal("C1 C1." + opName + "(C1 x, System.Int32 y)", c1M01.ToTestDisplayString()); + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + Assert.False(c1M01.IsMetadataVirtual()); + Assert.False(c1M01.IsMetadataFinal); + Assert.False(c1M01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.UserDefinedOperator, c1M01.MethodKind); + + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + } + else + { + Assert.Equal(MethodKind.UserDefinedOperator, c1M01.MethodKind); + Assert.Equal("C1 C1." + opName + "(C1 x, System.Int32 y)", c1M01.ToTestDisplayString()); + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + } + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + m01 = c2.Interfaces().Single().GetMembers().OfType().Single(); + var c2M01 = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01); + + Assert.True(c2M01.IsStatic); + Assert.False(c2M01.IsAbstract); + Assert.False(c2M01.IsVirtual); + Assert.False(c2M01.IsMetadataVirtual()); + Assert.False(c2M01.IsMetadataFinal); + Assert.False(c2M01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c2M01.MethodKind); + + Assert.Equal("C2 modopt(I1<>) C2.I1." + opName + "(C2 x, System.Int32 y)", c2M01.ToTestDisplayString()); + Assert.Equal(m01, c2M01.ExplicitInterfaceImplementations.Single()); + + Assert.Same(c2M01, c2.GetMembers().OfType().Where(m => !m.IsConstructor()).Single()); + } + + verifier.VerifyIL("C1.I1." + opName + "(C1, int)", +@" +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: call ""C1 C1." + opName + @"(C1, int)"" + IL_0007: ret +} +"); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticBinaryOperator_15([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=")] string op) + { + // A forwarding method isn't created if base class implements interface exactly the same way. + + var source1 = +@" +public partial interface I1 where T : I1 +{ + abstract static T operator " + op + @"(T x, C1 y); +} + +public partial class C1 +{ + public static C2 operator " + op + @"(C2 x, C1 y) => default; +} + +public class C2 : C1, I1 +{ +} +"; + + string matchingOp = MatchingBinaryOperator(op); + + if (matchingOp is object) + { + source1 += +@" +public partial interface I1 +{ + abstract static T operator " + matchingOp + @"(T x, C1 y); +} + +public partial class C1 +{ + public static C2 operator " + matchingOp + @"(C2 x, C1 y) => default; +} +"; + } + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C3 : C2, I1 +{ +} +"; + + var opName = BinaryOperatorName(op); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.EmitToImageReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + var m01 = c3.Interfaces().Single().GetMembers(opName).OfType().Single(); + + var c1M01 = c3.BaseType().BaseType().GetMember(opName); + Assert.Equal("C2 C1." + opName + "(C2 x, C1 y)", c1M01.ToTestDisplayString()); + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + Assert.False(c1M01.IsMetadataVirtual()); + Assert.False(c1M01.IsMetadataFinal); + Assert.False(c1M01.IsMetadataNewSlot()); + + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + + if (c1M01.ContainingModule is PEModuleSymbol) + { + var c2M01 = (MethodSymbol)c3.FindImplementationForInterfaceMember(m01); + Assert.Equal("C2 C2.I1." + opName + "(C2 x, C1 y)", c2M01.ToTestDisplayString()); + Assert.Equal(m01, c2M01.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Same(c1M01, c3.FindImplementationForInterfaceMember(m01)); + } + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticBinaryOperator_16([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=")] string op) + { + // A new implicit implementation is properly considered. + + var source1 = +@" +public partial interface I1 where T : I1 +{ + abstract static T operator " + op + @"(T x, C1 y); +} + +public partial class C1 : I1 +{ + public static C2 operator " + op + @"(C2 x, C1 y) => default; +} + +public partial class C2 : C1 +{ + public static C2 operator " + op + @"(C2 x, C1 y) => default; +} +"; + string matchingOp = MatchingBinaryOperator(op); + + if (matchingOp is object) + { + source1 += +@" +public partial interface I1 +{ + abstract static T operator " + matchingOp + @"(T x, C1 y); +} + +public partial class C1 : I1 +{ + public static C2 operator " + matchingOp + @"(C2 x, C1 y) => default; +} + +public partial class C2 : C1 +{ + public static C2 operator " + matchingOp + @"(C2 x, C1 y) => default; +} +"; + } + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C3 : C2, I1 +{ +} +"; + + var opName = BinaryOperatorName(op); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + var verifier = CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("C3.I1." + opName + "(C2, C1)", +@" +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: call ""C2 C2." + opName + @"(C2, C1)"" + IL_0007: ret +} +"); + + compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.EmitToImageReference() }); + + verifier = CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("C3.I1." + opName + "(C2, C1)", +@" +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: call ""C2 C2." + opName + @"(C2, C1)"" + IL_0007: ret +} +"); + + void validate(ModuleSymbol module) + { + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + var m01 = c3.Interfaces().Single().GetMembers(opName).OfType().Single(); + + var c2M01 = c3.BaseType().GetMember(opName); + Assert.Equal("C2 C2." + opName + "(C2 x, C1 y)", c2M01.ToTestDisplayString()); + + Assert.True(c2M01.IsStatic); + Assert.False(c2M01.IsAbstract); + Assert.False(c2M01.IsVirtual); + Assert.False(c2M01.IsMetadataVirtual()); + Assert.False(c2M01.IsMetadataFinal); + Assert.False(c2M01.IsMetadataNewSlot()); + + Assert.Empty(c2M01.ExplicitInterfaceImplementations); + + if (module is PEModuleSymbol) + { + var c3M01 = (MethodSymbol)c3.FindImplementationForInterfaceMember(m01); + Assert.Equal("C2 C3.I1." + opName + "(C2 x, C1 y)", c3M01.ToTestDisplayString()); + Assert.Equal(m01, c3M01.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Same(c2M01, c3.FindImplementationForInterfaceMember(m01)); + } + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticBinaryOperator_18([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=")] string op, bool genericFirst) + { + // An "ambiguity" in implicit implementation declared in generic base class plus interface is generic too. + + var generic = +@" + public static C1 operator " + op + @"(C1 x, U y) => default; +"; + var nonGeneric = +@" + public static C1 operator " + op + @"(C1 x, int y) => default; +"; + var source1 = +@" +public partial interface I1 where T : I1 +{ + abstract static T operator " + op + @"(T x, U y); +} + +public partial class C1 : I1, U> +{ +" + (genericFirst ? generic + nonGeneric : nonGeneric + generic) + @" +} +"; + + string matchingOp = MatchingBinaryOperator(op); + + if (matchingOp is object) + { + source1 += +@" +public partial interface I1 +{ + abstract static T operator " + matchingOp + @"(T x, U y); +} + +public partial class C1 +{ + public static C1 operator " + matchingOp + @"(C1 x, U y) => default; + public static C1 operator " + matchingOp + @"(C1 x, int y) => default; +} +"; + } + + var opName = BinaryOperatorName(op); + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { CreateCompilation("", targetFramework: TargetFramework.NetCoreApp).ToMetadataReference() }); + + compilation1.VerifyDiagnostics(); + Assert.Equal(2, compilation1.GlobalNamespace.GetTypeMember("C1").GetMembers().Where(m => m.Name.Contains(opName)).Count()); + + var source2 = +@" +public class C2 : C1, I1, int> +{ +} +"; + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.EmitToImageReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + var m01 = c2.Interfaces().Single().GetMembers(opName).OfType().Single(); + + Assert.True(m01.ContainingModule is RetargetingModuleSymbol or PEModuleSymbol); + + var c1M01 = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01); + Assert.Equal("C1 C1." + opName + "(C1 x, U y)", c1M01.OriginalDefinition.ToTestDisplayString()); + + var baseI1M01 = c2.BaseType().FindImplementationForInterfaceMember(m01); + Assert.Equal("C1 C1." + opName + "(C1 x, U y)", baseI1M01.OriginalDefinition.ToTestDisplayString()); + + Assert.Equal(c1M01, baseI1M01); + + if (c1M01.OriginalDefinition.ContainingModule is PEModuleSymbol) + { + Assert.Equal(m01, c1M01.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + } + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticBinaryOperator_20([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=")] string op, bool genericFirst) + { + // Same as ImplementAbstractStaticBinaryOperator_18 only implementation is explicit in source. + + var generic = +@" + static C1 I1, U>.operator " + op + @"(C1 x, U y) => default; +"; + var nonGeneric = +@" + public static C1 operator " + op + @"(C1 x, int y) => default; +"; + var source1 = +@" +public partial interface I1 where T : I1 +{ + abstract static T operator " + op + @"(T x, U y); +} + +public partial class C1 : I1, U> +{ +" + (genericFirst ? generic + nonGeneric : nonGeneric + generic) + @" +} +"; + string matchingOp = MatchingBinaryOperator(op); + + if (matchingOp is object) + { + source1 += +@" +public partial interface I1 where T : I1 +{ + abstract static T operator " + matchingOp + @"(T x, U y); +} + +public partial class C1 : I1, U> +{ + public static C1 operator " + matchingOp + @"(C1 x, int y) => default; + static C1 I1, U>.operator " + matchingOp + @"(C1 x, U y) => default; +} +"; + } + + var opName = BinaryOperatorName(op); + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { CreateCompilation("", targetFramework: TargetFramework.NetCoreApp).ToMetadataReference() }); + + compilation1.VerifyDiagnostics(); + Assert.Equal(2, compilation1.GlobalNamespace.GetTypeMember("C1").GetMembers().Where(m => m.Name.Contains(opName)).Count()); + + var source2 = +@" +public class C2 : C1, I1, int> +{ +} +"; + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.EmitToImageReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + var m01 = c2.Interfaces().Single().GetMembers(opName).OfType().Single(); + + Assert.True(m01.ContainingModule is RetargetingModuleSymbol or PEModuleSymbol); + + var c1M01 = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01); + Assert.Equal("C1 C1.I1, U>." + opName + "(C1 x, U y)", c1M01.OriginalDefinition.ToTestDisplayString()); + + Assert.Equal(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(c1M01, c2.BaseType().FindImplementationForInterfaceMember(m01)); + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticBinaryOperator_22([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=")] string op, bool genericFirst) + { + // Same as ImplementAbstractStaticMethod_18 only implicit implementation is in an intermediate base. + + var generic = +@" + public static C11 operator " + op + @"(C11 x, C1 y) => default; +"; + var nonGeneric = +@" + public static C11 operator " + op + @"(C11 x, C1 y) => default; +"; + var source1 = +@" +public partial interface I1 where T : I1 +{ + abstract static T operator " + op + @"(T x, U y); +} + +public partial class C1 +{ +" + (genericFirst ? generic + nonGeneric : nonGeneric + generic) + @" +} + +public class C11 : C1, I1, C1> +{ +} +"; + string matchingOp = MatchingBinaryOperator(op); + + if (matchingOp is object) + { + source1 += +@" +public partial interface I1 +{ + abstract static T operator " + matchingOp + @"(T x, U y); +} + +public partial class C1 +{ + public static C11 operator " + matchingOp + @"(C11 x, C1 y) => default; + public static C11 operator " + matchingOp + @"(C11 x, C1 y) => default; +} +"; + } + + var opName = BinaryOperatorName(op); + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { CreateCompilation("", targetFramework: TargetFramework.NetCoreApp).ToMetadataReference() }); + + compilation1.VerifyDiagnostics(); + Assert.Equal(2, compilation1.GlobalNamespace.GetTypeMember("C1").GetMembers().Where(m => m.Name.Contains(opName)).Count()); + + var source2 = +@" +public class C2 : C11, I1, C1> +{ +} +"; + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.EmitToImageReference() }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + var m01 = c2.Interfaces().Single().GetMembers(opName).OfType().Single(); + + Assert.True(m01.ContainingModule is RetargetingModuleSymbol or PEModuleSymbol); + + var c1M01 = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01); + var expectedDisplay = m01.ContainingModule is PEModuleSymbol ? "C11 C11.I1, C1>." + opName + "(C11 x, C1 y)" : "C11 C1." + opName + "(C11 x, C1 y)"; + Assert.Equal(expectedDisplay, c1M01.OriginalDefinition.ToTestDisplayString()); + + var baseI1M01 = c2.BaseType().FindImplementationForInterfaceMember(m01); + Assert.Equal(expectedDisplay, baseI1M01.OriginalDefinition.ToTestDisplayString()); + + Assert.Equal(c1M01, baseI1M01); + + if (c1M01.OriginalDefinition.ContainingModule is PEModuleSymbol) + { + Assert.Equal(m01, c1M01.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + } + } + } + + [Theory] + [CombinatorialData] + public void ExplicitImplementationModifiersUnaryOperator_01([CombinatorialValues("+", "-", "!", "~", "++", "--", "true", "false")] string op) + { + var source1 = +@" +public interface I1 +{ + abstract static I1 operator " + op + @"(I1 x); +} + +class + C1 : I1 +{ + static I1 I1.operator " + op + @"(I1 x) => default; +} + +class + C2 : I1 +{ + private static I1 I1.operator " + op + @"(I1 x) => default; +} + +class + C3 : I1 +{ + protected static I1 I1.operator " + op + @"(I1 x) => default; +} + +class + C4 : I1 +{ + internal static I1 I1.operator " + op + @"(I1 x) => default; +} + +class + C5 : I1 +{ + protected internal static I1 I1.operator " + op + @"(I1 x) => default; +} + +class + C6 : I1 +{ + private protected static I1 I1.operator " + op + @"(I1 x) => default; +} + +class + C7 : I1 +{ + public static I1 I1.operator " + op + @"(I1 x) => default; +} + +class + C8 : I1 +{ + static partial I1 I1.operator " + op + @"(I1 x) => default; +} + +class + C9 : I1 +{ + async static I1 I1.operator " + op + @"(I1 x) => default; +} + +class + C10 : I1 +{ + unsafe static I1 I1.operator " + op + @"(I1 x) => default; +} + +class + C11 : I1 +{ + static readonly I1 I1.operator " + op + @"(I1 x) => default; +} + +class + C12 : I1 +{ + extern static I1 I1.operator " + op + @"(I1 x); +} + +class + C13 : I1 +{ + abstract static I1 I1.operator " + op + @"(I1 x) => default; +} + +class + C14 : I1 +{ + virtual static I1 I1.operator " + op + @"(I1 x) => default; +} + +class + C15 : I1 +{ + sealed static I1 I1.operator " + op + @"(I1 x) => default; +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll.WithAllowUnsafe(true), + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + + Assert.Equal(Accessibility.Private, c1.GetMembers().OfType().Where(m => !m.IsConstructor()).Single().DeclaredAccessibility); + + compilation1.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.WRN_ExternMethodNoImplementation or (int)ErrorCode.ERR_OpTFRetType or (int)ErrorCode.ERR_OperatorNeedsMatch)).Verify( + // (16,35): error CS0106: The modifier 'private' is not valid for this item + // private static I1 I1.operator !(I1 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("private").WithLocation(16, 35), + // (22,37): error CS0106: The modifier 'protected' is not valid for this item + // protected static I1 I1.operator !(I1 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("protected").WithLocation(22, 37), + // (28,36): error CS0106: The modifier 'internal' is not valid for this item + // internal static I1 I1.operator !(I1 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("internal").WithLocation(28, 36), + // (34,46): error CS0106: The modifier 'protected internal' is not valid for this item + // protected internal static I1 I1.operator !(I1 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("protected internal").WithLocation(34, 46), + // (40,45): error CS0106: The modifier 'private protected' is not valid for this item + // private protected static I1 I1.operator !(I1 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("private protected").WithLocation(40, 45), + // (46,34): error CS0106: The modifier 'public' is not valid for this item + // public static I1 I1.operator !(I1 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("public").WithLocation(46, 34), + // (52,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // static partial I1 I1.operator !(I1 x) => default; + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(52, 12), + // (58,33): error CS0106: The modifier 'async' is not valid for this item + // async static I1 I1.operator !(I1 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("async").WithLocation(58, 33), + // (70,36): error CS0106: The modifier 'readonly' is not valid for this item + // static readonly I1 I1.operator !(I1 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(70, 36), + // (82,36): error CS0106: The modifier 'abstract' is not valid for this item + // abstract static I1 I1.operator !(I1 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(82, 36), + // (88,35): error CS0106: The modifier 'virtual' is not valid for this item + // virtual static I1 I1.operator !(I1 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("virtual").WithLocation(88, 35), + // (94,34): error CS0106: The modifier 'sealed' is not valid for this item + // sealed static I1 I1.operator !(I1 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(94, 34) + ); + } + + [Theory] + [CombinatorialData] + public void ExplicitImplementationModifiersBinaryOperator_01([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op) + { + var source1 = +@" +public interface I1 +{ + abstract static I1 operator " + op + @"(I1 x, int y); +} + +struct + C1 : I1 +{ + static I1 I1.operator " + op + @"(I1 x, int y) => default; +} + +struct + C2 : I1 +{ + private static I1 I1.operator " + op + @"(I1 x, int y) => default; +} + +struct + C3 : I1 +{ + protected static I1 I1.operator " + op + @"(I1 x, int y) => default; +} + +struct + C4 : I1 +{ + internal static I1 I1.operator " + op + @"(I1 x, int y) => default; +} + +struct + C5 : I1 +{ + protected internal static I1 I1.operator " + op + @"(I1 x, int y) => default; +} + +struct + C6 : I1 +{ + private protected static I1 I1.operator " + op + @"(I1 x, int y) => default; +} + +struct + C7 : I1 +{ + public static I1 I1.operator " + op + @"(I1 x, int y) => default; +} + +struct + C8 : I1 +{ + static partial I1 I1.operator " + op + @"(I1 x, int y) => default; +} + +struct + C9 : I1 +{ + async static I1 I1.operator " + op + @"(I1 x, int y) => default; +} + +struct + C10 : I1 +{ + unsafe static I1 I1.operator " + op + @"(I1 x, int y) => default; +} + +struct + C11 : I1 +{ + static readonly I1 I1.operator " + op + @"(I1 x, int y) => default; +} + +struct + C12 : I1 +{ + extern static I1 I1.operator " + op + @"(I1 x, int y); +} + +struct + C13 : I1 +{ + abstract static I1 I1.operator " + op + @"(I1 x, int y) => default; +} + +struct + C14 : I1 +{ + virtual static I1 I1.operator " + op + @"(I1 x, int y) => default; +} + +struct + C15 : I1 +{ + sealed static I1 I1.operator " + op + @"(I1 x, int y) => default; +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll.WithAllowUnsafe(true), + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + + Assert.Equal(Accessibility.Private, c1.GetMembers().OfType().Where(m => !m.IsConstructor()).Single().DeclaredAccessibility); + + compilation1.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.WRN_ExternMethodNoImplementation or (int)ErrorCode.ERR_OperatorNeedsMatch)).Verify( + // (16,35): error CS0106: The modifier 'private' is not valid for this item + // private static I1 I1.operator ^(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("private").WithLocation(16, 35), + // (22,37): error CS0106: The modifier 'protected' is not valid for this item + // protected static I1 I1.operator ^(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("protected").WithLocation(22, 37), + // (28,36): error CS0106: The modifier 'internal' is not valid for this item + // internal static I1 I1.operator ^(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("internal").WithLocation(28, 36), + // (34,46): error CS0106: The modifier 'protected internal' is not valid for this item + // protected internal static I1 I1.operator ^(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("protected internal").WithLocation(34, 46), + // (40,45): error CS0106: The modifier 'private protected' is not valid for this item + // private protected static I1 I1.operator ^(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("private protected").WithLocation(40, 45), + // (46,34): error CS0106: The modifier 'public' is not valid for this item + // public static I1 I1.operator ^(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("public").WithLocation(46, 34), + // (52,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // static partial I1 I1.operator ^(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(52, 12), + // (58,33): error CS0106: The modifier 'async' is not valid for this item + // async static I1 I1.operator ^(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("async").WithLocation(58, 33), + // (70,36): error CS0106: The modifier 'readonly' is not valid for this item + // static readonly I1 I1.operator ^(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(70, 36), + // (82,36): error CS0106: The modifier 'abstract' is not valid for this item + // abstract static I1 I1.operator ^(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(82, 36), + // (88,35): error CS0106: The modifier 'virtual' is not valid for this item + // virtual static I1 I1.operator ^(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("virtual").WithLocation(88, 35), + // (94,34): error CS0106: The modifier 'sealed' is not valid for this item + // sealed static I1 I1.operator ^(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(94, 34) + ); + } + + [Theory] + [CombinatorialData] + public void ExplicitInterfaceSpecifierErrorsUnaryOperator_01([CombinatorialValues("+", "-", "!", "~", "++", "--", "true", "false")] string op) + { + var source1 = +@" +public interface I1 where T : struct +{ + abstract static I1 operator " + op + @"(I1 x); +} + +class C1 +{ + static I1 I1.operator " + op + @"(I1 x) => default; +} + +class C2 : I1 +{ + static I1 I1.operator " + op + @"(I1 x) => default; +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll.WithAllowUnsafe(true), + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_OpTFRetType or (int)ErrorCode.ERR_OperatorNeedsMatch)).Verify( + // (9,20): error CS0540: 'C1.I1.operator -(I1)': containing type does not implement interface 'I1' + // static I1 I1.operator -(I1 x) => default; + Diagnostic(ErrorCode.ERR_ClassDoesntImplementInterface, "I1").WithArguments("C1.I1.operator " + op + "(I1)", "I1").WithLocation(9, 20), + // (12,7): error CS0453: The type 'C2' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'I1' + // class C2 : I1 + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "C2").WithArguments("I1", "T", "C2").WithLocation(12, 7), + // (14,19): error CS0453: The type 'C2' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'I1' + // static I1 I1.operator -(I1 x) => default; + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "I1").WithArguments("I1", "T", "C2").WithLocation(14, 19), + // (14,35): error CS0453: The type 'C2' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'I1' + // static I1 I1.operator -(I1 x) => default; + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, op).WithArguments("I1", "T", "C2").WithLocation(14, 35), + // (14,44): error CS0453: The type 'C2' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'I1' + // static I1 I1.operator -(I1 x) => default; + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "x").WithArguments("I1", "T", "C2").WithLocation(14, 44 + op.Length - 1) + ); + } + + [Theory] + [CombinatorialData] + public void ExplicitInterfaceSpecifierErrorsBinaryOperator_01([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op) + { + var source1 = +@" +public interface I1 where T : class +{ + abstract static I1 operator " + op + @"(I1 x, int y); +} + +struct C1 +{ + static I1 I1.operator " + op + @"(I1 x, int y) => default; +} + +struct C2 : I1 +{ + static I1 I1.operator " + op + @"(I1 x, int y) => default; +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll.WithAllowUnsafe(true), + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + // (9,23): error CS0540: 'C1.I1.operator %(I1, int)': containing type does not implement interface 'I1' + // static I1 I1.operator %(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_ClassDoesntImplementInterface, "I1").WithArguments("C1.I1.operator " + op + "(I1, int)", "I1").WithLocation(9, 23), + // (12,8): error CS0452: The type 'C2' must be a reference type in order to use it as parameter 'T' in the generic type or method 'I1' + // struct C2 : I1 + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "C2").WithArguments("I1", "T", "C2").WithLocation(12, 8), + // (14,19): error CS0452: The type 'C2' must be a reference type in order to use it as parameter 'T' in the generic type or method 'I1' + // static I1 I1.operator %(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "I1").WithArguments("I1", "T", "C2").WithLocation(14, 19), + // (14,35): error CS0452: The type 'C2' must be a reference type in order to use it as parameter 'T' in the generic type or method 'I1' + // static I1 I1.operator %(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, op).WithArguments("I1", "T", "C2").WithLocation(14, 35), + // (14,44): error CS0452: The type 'C2' must be a reference type in order to use it as parameter 'T' in the generic type or method 'I1' + // static I1 I1.operator %(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "x").WithArguments("I1", "T", "C2").WithLocation(14, 44 + op.Length - 1) + ); + } } } From 5a0ac29e4580ee66dbf154929f8feb7e8c5b92d0 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Fri, 7 May 2021 14:06:23 -0700 Subject: [PATCH 069/127] Update to "Computing..." with spinner --- .../Core/Def/ServicesVSResources.resx | 4 + .../ValueTracking/ComputingTreeViewItem.cs | 18 ++++ ...ngTreeItemViewModel.cs => TreeViewItem.cs} | 15 +++- .../Def/ValueTracking/TreeViewItemBase.cs | 9 +- .../ValueTrackedTreeItemViewModel.cs | 33 +++++--- .../ValueTrackingCommandHandler.cs | 4 +- .../ValueTracking/ValueTrackingToolWindow.cs | 2 +- .../Def/ValueTracking/ValueTrackingTree.xaml | 83 +++++++++++-------- .../ValueTracking/ValueTrackingTree.xaml.cs | 2 +- .../ValueTrackingTreeViewModel.cs | 6 +- .../Core/Def/xlf/ServicesVSResources.cs.xlf | 5 ++ .../Core/Def/xlf/ServicesVSResources.de.xlf | 5 ++ .../Core/Def/xlf/ServicesVSResources.es.xlf | 5 ++ .../Core/Def/xlf/ServicesVSResources.fr.xlf | 5 ++ .../Core/Def/xlf/ServicesVSResources.it.xlf | 5 ++ .../Core/Def/xlf/ServicesVSResources.ja.xlf | 5 ++ .../Core/Def/xlf/ServicesVSResources.ko.xlf | 5 ++ .../Core/Def/xlf/ServicesVSResources.pl.xlf | 5 ++ .../Def/xlf/ServicesVSResources.pt-BR.xlf | 5 ++ .../Core/Def/xlf/ServicesVSResources.ru.xlf | 5 ++ .../Core/Def/xlf/ServicesVSResources.tr.xlf | 5 ++ .../Def/xlf/ServicesVSResources.zh-Hans.xlf | 5 ++ .../Def/xlf/ServicesVSResources.zh-Hant.xlf | 5 ++ 23 files changed, 185 insertions(+), 56 deletions(-) create mode 100644 src/VisualStudio/Core/Def/ValueTracking/ComputingTreeViewItem.cs rename src/VisualStudio/Core/Def/ValueTracking/{ValueTrackingTreeItemViewModel.cs => TreeViewItem.cs} (92%) diff --git a/src/VisualStudio/Core/Def/ServicesVSResources.resx b/src/VisualStudio/Core/Def/ServicesVSResources.resx index 01a2a5225903f..686a37fa2e02d 100644 --- a/src/VisualStudio/Core/Def/ServicesVSResources.resx +++ b/src/VisualStudio/Core/Def/ServicesVSResources.resx @@ -1711,4 +1711,8 @@ I agree to all of the foregoing: Value Tracking Title of the "Value Tracking" tool window. "Value" is the variable/symbol and "Tracking" is the action the tool is doing to follow where the value can originate from. + + Calculating... + Used in UI to represent progress in the context of loading items. + \ No newline at end of file diff --git a/src/VisualStudio/Core/Def/ValueTracking/ComputingTreeViewItem.cs b/src/VisualStudio/Core/Def/ValueTracking/ComputingTreeViewItem.cs new file mode 100644 index 0000000000000..0fcf993b01616 --- /dev/null +++ b/src/VisualStudio/Core/Def/ValueTracking/ComputingTreeViewItem.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Windows.Threading; + +namespace Microsoft.VisualStudio.LanguageServices.ValueTracking +{ + internal class ComputingTreeViewItem : TreeViewItemBase + { + public string Text => ServicesVSResources.Calculating; + + public ComputingTreeViewItem() + { + } + } +} diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/TreeViewItem.cs similarity index 92% rename from src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs rename to src/VisualStudio/Core/Def/ValueTracking/TreeViewItem.cs index d5a39de1d9a93..480e75e00aaa5 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/TreeViewItem.cs @@ -20,7 +20,7 @@ namespace Microsoft.VisualStudio.LanguageServices.ValueTracking { - internal class ValueTrackingTreeItemViewModel : TreeViewItemBase + internal class TreeViewItem : TreeViewItemBase { private readonly SourceText _sourceText; private readonly ISymbol _symbol; @@ -38,6 +38,7 @@ internal class ValueTrackingTreeItemViewModel : TreeViewItemBase public string FileName => Document.FilePath ?? Document.Name; public ImageSource GlyphImage => _symbol.GetGlyph().GetImageSource(_glyphService); + public bool ShowGlyph => !IsLoading; public ImmutableArray ClassifiedSpans { get; } @@ -74,7 +75,7 @@ public IList Inlines } } - public ValueTrackingTreeItemViewModel( + public TreeViewItem( Document document, TextSpan textSpan, SourceText sourceText, @@ -83,7 +84,7 @@ public ValueTrackingTreeItemViewModel( ValueTrackingTreeViewModel treeViewModel, IGlyphService glyphService, IThreadingContext threadingContext, - ImmutableArray children = default) + ImmutableArray children = default) { Document = document; TextSpan = textSpan; @@ -107,6 +108,14 @@ public ValueTrackingTreeItemViewModel( sourceText.GetLineAndOffset(textSpan.Start, out var lineStart, out var _); sourceText.GetLineAndOffset(textSpan.End, out var lineEnd, out var _); LineSpan = LineSpan.FromBounds(lineStart, lineEnd); + + PropertyChanged += (s, e) => + { + if (e.PropertyName == nameof(IsLoading)) + { + NotifyPropertyChanged(nameof(ShowGlyph)); + } + }; } public virtual void Select() diff --git a/src/VisualStudio/Core/Def/ValueTracking/TreeViewItemBase.cs b/src/VisualStudio/Core/Def/ValueTracking/TreeViewItemBase.cs index d7cb529bc40e1..1ed2cc03c7ac8 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/TreeViewItemBase.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/TreeViewItemBase.cs @@ -11,7 +11,7 @@ namespace Microsoft.VisualStudio.LanguageServices.ValueTracking internal class TreeViewItemBase : INotifyPropertyChanged { private bool _isExpanded = false; - public bool IsNodeExpanded + public virtual bool IsNodeExpanded { get => _isExpanded; set => SetProperty(ref _isExpanded, value); @@ -24,6 +24,13 @@ public bool IsNodeSelected set => SetProperty(ref _isSelected, value); } + private bool _isLoading; + public bool IsLoading + { + get => _isLoading; + set => SetProperty(ref _isLoading, value); + } + public event PropertyChangedEventHandler? PropertyChanged; protected void SetProperty(ref T field, T value, [CallerMemberName] string name = "") diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs index 15febb3e50824..1289f429b5f8c 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs @@ -13,7 +13,7 @@ namespace Microsoft.VisualStudio.LanguageServices.ValueTracking { - internal class ValueTrackedTreeItemViewModel : ValueTrackingTreeItemViewModel + internal class ValueTrackedTreeItemViewModel : TreeViewItem { private bool _childrenCalculated; private readonly Solution _solution; @@ -21,6 +21,16 @@ internal class ValueTrackedTreeItemViewModel : ValueTrackingTreeItemViewModel private readonly IValueTrackingService _valueTrackingService; private readonly ValueTrackedItem _trackedItem; + public override bool IsNodeExpanded + { + get => base.IsNodeExpanded; + set + { + base.IsNodeExpanded = value; + CalculateChildren(); + } + } + public ValueTrackedTreeItemViewModel( ValueTrackedItem trackedItem, Solution solution, @@ -28,7 +38,7 @@ public ValueTrackedTreeItemViewModel( IGlyphService glyphService, IValueTrackingService valueTrackingService, IThreadingContext threadingContext, - ImmutableArray children = default) + ImmutableArray children = default) : base( trackedItem.Document, trackedItem.Span, @@ -56,14 +66,6 @@ public ValueTrackedTreeItemViewModel( { NotifyPropertyChanged(nameof(ChildItems)); }; - - PropertyChanged += (s, a) => - { - if (a.PropertyName == nameof(IsNodeExpanded)) - { - CalculateChildren(); - } - }; } } @@ -71,13 +73,17 @@ private void CalculateChildren() { ThreadHelper.ThrowIfNotOnUIThread(); - if (_childrenCalculated) + if (_childrenCalculated || IsLoading) { return; } TreeViewModel.LoadingCount++; - _childrenCalculated = true; + IsLoading = true; + ChildItems.Clear(); + + var computingItem = new ComputingTreeViewItem(); + ChildItems.Add(computingItem); System.Threading.Tasks.Task.Run(async () => { @@ -86,6 +92,7 @@ private void CalculateChildren() var items = await _valueTrackingService.TrackValueSourceAsync(_trackedItem, ThreadingContext.DisposalToken).ConfigureAwait(false); await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(); + ChildItems.Clear(); foreach (var valueTrackedItem in items) @@ -103,6 +110,8 @@ private void CalculateChildren() { await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(); TreeViewModel.LoadingCount--; + _childrenCalculated = true; + IsLoading = false; } }, ThreadingContext.DisposalToken); } diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs index e62db801d00c1..ec76feedce86a 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs @@ -163,7 +163,7 @@ private async Task ShowToolWindowAsync(ITextView textView, ISymbol selectedSymbo var documentSpan = await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync(document, location.SourceSpan, cancellationToken).ConfigureAwait(false); var classificationResult = await ClassifiedSpansAndHighlightSpanFactory.ClassifyAsync(documentSpan, cancellationToken).ConfigureAwait(false); - var root = new ValueTrackingTreeItemViewModel( + var root = new TreeViewItem( document, location.SourceSpan, sourceText, @@ -181,7 +181,7 @@ private async Task ShowToolWindowAsync(ITextView textView, ISymbol selectedSymbo await ShowToolWindowAsync(cancellationToken).ConfigureAwait(true); - ValueTrackingTreeItemViewModel CreateViewModel(ValueTrackedItem valueTrackedItem, ImmutableArray children = default) + TreeViewItem CreateViewModel(ValueTrackedItem valueTrackedItem, ImmutableArray children = default) => new ValueTrackedTreeItemViewModel( valueTrackedItem, solution, diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs index d0fbf3a5abccf..d8f5a36446a36 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs @@ -60,7 +60,7 @@ public ValueTrackingToolWindow(ValueTrackingTreeViewModel viewModel) ViewModel = viewModel; } - public ValueTrackingTreeItemViewModel? Root + public TreeViewItem? Root { get => ViewModel?.Roots.Single(); set diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml index 14a6b5f9372ae..6b2037a95d8c0 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml @@ -20,46 +20,63 @@ - - - + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs index d9a7e73fd46e6..93d3f87488af5 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs @@ -34,7 +34,7 @@ public ValueTrackingTree(ValueTrackingTreeViewModel viewModel) private void ValueTrackingTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs e) { - _viewModel.SelectedItem = (ValueTrackingTreeItemViewModel)e.NewValue; + _viewModel.SelectedItem = (TreeViewItem)e.NewValue; } } } diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs index 0e2b81d8b790d..80f908a932269 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs @@ -25,10 +25,10 @@ public Brush? HighlightBrush public IClassificationFormatMap ClassificationFormatMap { get; } public ClassificationTypeMap ClassificationTypeMap { get; } public IEditorFormatMapService FormatMapService { get; } - public ObservableCollection Roots { get; } = new(); + public ObservableCollection Roots { get; } = new(); - private ValueTrackingTreeItemViewModel? _selectedItem; - public ValueTrackingTreeItemViewModel? SelectedItem + private TreeViewItem? _selectedItem; + public TreeViewItem? SelectedItem { get => _selectedItem; set => SetProperty(ref _selectedItem, value); diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf index a0f324b64b5e6..d43198a43304b 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf @@ -147,6 +147,11 @@ Klient jazyka diagnostiky C# nebo Visual Basic + + Calculating... + Calculating... + Used in UI to represent progress in the context of loading items. + Calculating dependents... Počítají se závislosti... diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf index d1cf61d8504bc..dbdb202588c2e 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf @@ -147,6 +147,11 @@ Client für C#-/Visual Basic-Diagnosesprache + + Calculating... + Calculating... + Used in UI to represent progress in the context of loading items. + Calculating dependents... Abhängige Objekte werden berechnet... diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf index e4cbc039f039f..787a462281b80 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf @@ -147,6 +147,11 @@ Cliente de lenguaje de diagnóstico de C#/Visual Basic + + Calculating... + Calculating... + Used in UI to represent progress in the context of loading items. + Calculating dependents... Calculando dependientes... diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf index 24945d5d92815..698e2597e2664 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf @@ -147,6 +147,11 @@ Client de langage de diagnostics C#/Visual Basic + + Calculating... + Calculating... + Used in UI to represent progress in the context of loading items. + Calculating dependents... Calcul des dépendants... diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf index 16f4e1f481ea2..0b9a20bac4fa9 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf @@ -147,6 +147,11 @@ Client del linguaggio di diagnostica C#/Visual Basic + + Calculating... + Calculating... + Used in UI to represent progress in the context of loading items. + Calculating dependents... Calcolo dei dipendenti... diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf index e993485c2ba1e..8e522b55c98d8 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf @@ -147,6 +147,11 @@ C#/Visual Basic 診断言語クライアント + + Calculating... + Calculating... + Used in UI to represent progress in the context of loading items. + Calculating dependents... 依存を計算しています... diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf index e29d269d6deb3..540a6a3ca3910 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf @@ -147,6 +147,11 @@ C#/Visual Basic 진단 언어 클라이언트 + + Calculating... + Calculating... + Used in UI to represent progress in the context of loading items. + Calculating dependents... 종속 항목을 계산하는 중... diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf index d7574a861f577..2887a21f165a2 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf @@ -147,6 +147,11 @@ Klient języka diagnostyki języka C#/Visual Basic + + Calculating... + Calculating... + Used in UI to represent progress in the context of loading items. + Calculating dependents... Obliczanie elementów zależnych... diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf index 095df6b896b23..914cf904a5938 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf @@ -147,6 +147,11 @@ Cliente da Linguagem de Diagnóstico C#/Visual Basic + + Calculating... + Calculating... + Used in UI to represent progress in the context of loading items. + Calculating dependents... Calculando dependentes... diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf index 2aa5f3492d5e5..5dbc0f14e29a5 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf @@ -147,6 +147,11 @@ Языковой клиент диагностики C#/Visual Basic + + Calculating... + Calculating... + Used in UI to represent progress in the context of loading items. + Calculating dependents... Вычисление зависимостей… diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf index e46a62ba8c49c..9af9cabfbc65e 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf @@ -147,6 +147,11 @@ C#/Visual Basic Tanılama Dili İstemcisi + + Calculating... + Calculating... + Used in UI to represent progress in the context of loading items. + Calculating dependents... Bağımlılar hesaplanıyor... diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf index 5eab14e05c6fb..9f8c0776196dc 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf @@ -147,6 +147,11 @@ C#/Visual Basic 语言服务器客户端 + + Calculating... + Calculating... + Used in UI to represent progress in the context of loading items. + Calculating dependents... 正在计算依赖项... diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf index dca99fbdf2ea5..fda097d1d56b8 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf @@ -147,6 +147,11 @@ C#/Visual Basic 診斷語言用戶端 + + Calculating... + Calculating... + Used in UI to represent progress in the context of loading items. + Calculating dependents... 正在計算相依項... From 983394ae26dce7673c94895e0302e144998fc466 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Fri, 7 May 2021 15:38:55 -0700 Subject: [PATCH 070/127] Move to static ValueTracker to prepare for OOP code --- ...=> ValueTracker.FindReferencesProgress.cs} | 2 +- ....cs => ValueTracker.OperationCollector.cs} | 6 +- .../Core/ValueTracking/ValueTracker.cs | 313 ++++++++++++++++++ .../ValueTracking/ValueTrackingService.cs | 301 +---------------- .../RemoteValueTrackingService.cs | 14 + 5 files changed, 335 insertions(+), 301 deletions(-) rename src/EditorFeatures/Core/ValueTracking/{ValueTrackingService.FindReferencesProgress.cs => ValueTracker.FindReferencesProgress.cs} (99%) rename src/EditorFeatures/Core/ValueTracking/{ValueTrackingService.Visitor.cs => ValueTracker.OperationCollector.cs} (98%) create mode 100644 src/EditorFeatures/Core/ValueTracking/ValueTracker.cs create mode 100644 src/Workspaces/Remote/ServiceHub/Services/ValueTracking/RemoteValueTrackingService.cs diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs b/src/EditorFeatures/Core/ValueTracking/ValueTracker.FindReferencesProgress.cs similarity index 99% rename from src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs rename to src/EditorFeatures/Core/ValueTracking/ValueTracker.FindReferencesProgress.cs index c355b1eead471..017373779b62a 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.FindReferencesProgress.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTracker.FindReferencesProgress.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.ValueTracking { - internal partial class ValueTrackingService + internal static partial class ValueTracker { private class FindReferencesProgress : IStreamingFindReferencesProgress, IStreamingProgressTracker { diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs b/src/EditorFeatures/Core/ValueTracking/ValueTracker.OperationCollector.cs similarity index 98% rename from src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs rename to src/EditorFeatures/Core/ValueTracking/ValueTracker.OperationCollector.cs index b874cd7e162d5..96d4234c5d623 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.Visitor.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTracker.OperationCollector.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.ValueTracking { - internal partial class ValueTrackingService + internal static partial class ValueTracker { private class OperationCollector { @@ -43,8 +43,8 @@ IFieldReferenceOperation or IMethodBodyOperation methodBodyOperation => VisitReturnDescendentsAsync(methodBodyOperation, allowImplicit: true, cancellationToken), IBlockOperation blockOperation => VisitReturnDescendentsAsync(blockOperation, allowImplicit: false, cancellationToken), - // Default to reporting if there is symbol information available - _ => VisitDefaultAsync(operation, cancellationToken) + // Default to reporting if there is symbol information available + _ => VisitDefaultAsync(operation, cancellationToken) }; private async Task VisitReturnDescendentsAsync(IOperation operation, bool allowImplicit, CancellationToken cancellationToken) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTracker.cs b/src/EditorFeatures/Core/ValueTracking/ValueTracker.cs new file mode 100644 index 0000000000000..8e62ece5859eb --- /dev/null +++ b/src/EditorFeatures/Core/ValueTracking/ValueTracker.cs @@ -0,0 +1,313 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Shared.Extensions; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Text; +using System.Threading; +using System.Linq; +using Microsoft.CodeAnalysis.LanguageServices; +using Microsoft.CodeAnalysis.Operations; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.FindSymbols; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ValueTracking +{ + internal static partial class ValueTracker + { + public static async Task TrackValueSourceInternalAsync( + TextSpan selection, + Document document, + ValueTrackingProgressCollector progressCollector, + CancellationToken cancellationToken) + { + var (symbol, node) = await GetSelectedSymbolAsync(selection, document, cancellationToken).ConfigureAwait(false); + var operationCollector = new OperationCollector(progressCollector, document.Project.Solution); + + if (symbol + is IPropertySymbol + or IFieldSymbol + or ILocalSymbol + or IParameterSymbol) + { + RoslynDebug.AssertNotNull(node); + + var solution = document.Project.Solution; + var declaringSyntaxReferences = symbol.DeclaringSyntaxReferences; + var syntaxFacts = document.GetRequiredLanguageService(); + + // If the selection is within a declaration of the symbol, we want to include + // all declarations and assignments of the symbol + if (declaringSyntaxReferences.Any(r => r.Span.IntersectsWith(selection))) + { + // Add all initializations of the symbol. Those are not caught in + // the reference finder but should still show up in the tree + foreach (var syntaxRef in declaringSyntaxReferences) + { + var location = Location.Create(syntaxRef.SyntaxTree, syntaxRef.Span); + await progressCollector.TryReportAsync(solution, location, symbol, cancellationToken).ConfigureAwait(false); + } + + await TrackVariableReferencesAsync(symbol, operationCollector, cancellationToken).ConfigureAwait(false); + } + // The selection is not on a declaration, check that the node + // is on the left side of an assignment. If so, populate so we can + // track the RHS values that contribute to this value + else if (syntaxFacts.IsLeftSideOfAnyAssignment(node)) + { + await AddItemsFromAssignmentAsync(document, node, operationCollector, cancellationToken).ConfigureAwait(false); + } + // Not on the left part of an assignment? Then just add an item with the statement + // and the symbol. It should be the top item, and children will find the sources + // of the value. A good example is a return statement, such as "return $$x", + // where $$ is the cursor position. The top item should have the return statement for + // context, and the remaining items should expand into the assignments of x + else + { + await progressCollector.TryReportAsync(document.Project.Solution, node.GetLocation(), symbol, cancellationToken).ConfigureAwait(false); + } + } + } + + public static async Task TrackValueSourceInternalAsync( + ValueTrackedItem previousTrackedItem, + ValueTrackingProgressCollector progressCollector, + CancellationToken cancellationToken) + { + progressCollector.Parent = previousTrackedItem; + var operationCollector = new OperationCollector(progressCollector, previousTrackedItem.Document.Project.Solution); + + switch (previousTrackedItem.Symbol) + { + case ILocalSymbol: + case IPropertySymbol: + case IFieldSymbol: + { + // The "output" is a variable assignment, track places where it gets assigned and defined + await TrackVariableDefinitionsAsync(previousTrackedItem.Symbol, operationCollector, cancellationToken).ConfigureAwait(false); + await TrackVariableReferencesAsync(previousTrackedItem.Symbol, operationCollector, cancellationToken).ConfigureAwait(false); + } + break; + + case IParameterSymbol parameterSymbol: + { + var previousSymbol = previousTrackedItem.Parent?.Symbol; + + // If the current parameter is a parameter symbol for the previous tracked method it should be treated differently. + // For example: + // string PrependString(string pre, string s) => pre + s; + // ^--- previously tracked ^---- current parameter being tracked + // + // In this case, s is being tracked because it contributed to the return of the method. We only + // want to track assignments to s that could impact the return rather than tracking the same method + // twice. + var isParameterForPreviousTrackedMethod = previousSymbol?.Equals(parameterSymbol.ContainingSymbol) == true; + + // For Ref or Out parameters, they contribute data across method calls through assignments + // within the method. No need to track returns + // Ex: TryGetValue("mykey", out var [|v|]) + // [|v|] is the interesting part, we don't care what the method returns + var isRefOrOut = parameterSymbol.IsRefOrOut(); + + // Always track the parameter assignments as variables, in case they are assigned anywhere in the method + await TrackVariableReferencesAsync(parameterSymbol, operationCollector, cancellationToken).ConfigureAwait(false); + + var trackMethod = !(isParameterForPreviousTrackedMethod || isRefOrOut); + if (trackMethod) + { + await TrackParameterSymbolAsync(parameterSymbol, operationCollector, cancellationToken).ConfigureAwait(false); + } + } + break; + + case IMethodSymbol methodSymbol: + { + // The "output" is from a method, meaning it has a return or out param that is used. Track those + await TrackMethodSymbolAsync(methodSymbol, operationCollector, cancellationToken).ConfigureAwait(false); + } + break; + } + } + + private static async Task AddItemsFromAssignmentAsync(Document document, SyntaxNode lhsNode, OperationCollector collector, CancellationToken cancellationToken) + { + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var operation = semanticModel.GetOperation(lhsNode, cancellationToken); + if (operation is null) + { + return; + } + + IAssignmentOperation? assignmentOperation = null; + + while (assignmentOperation is null + && operation is not null) + { + assignmentOperation = operation as IAssignmentOperation; + operation = operation.Parent; + } + + if (assignmentOperation is null) + { + return; + } + + await collector.VisitAsync(assignmentOperation, cancellationToken).ConfigureAwait(false); + } + + private static async Task TrackVariableReferencesAsync(ISymbol symbol, OperationCollector collector, CancellationToken cancellationToken) + { + var findReferenceProgressCollector = new FindReferencesProgress(collector); + await SymbolFinder.FindReferencesAsync( + symbol, + collector.Solution, + findReferenceProgressCollector, + documents: null, FindReferencesSearchOptions.Default, cancellationToken).ConfigureAwait(false); + } + + private static async Task TrackParameterSymbolAsync( + IParameterSymbol parameterSymbol, + OperationCollector collector, + CancellationToken cancellationToken) + { + var containingMethod = (IMethodSymbol)parameterSymbol.ContainingSymbol; + var findReferenceProgressCollector = new FindReferencesProgress(collector); + await SymbolFinder.FindReferencesAsync( + containingMethod, + collector.Solution, + findReferenceProgressCollector, + documents: null, FindReferencesSearchOptions.Default, cancellationToken).ConfigureAwait(false); + } + + private static async Task TrackMethodSymbolAsync(IMethodSymbol methodSymbol, OperationCollector collector, CancellationToken cancellationToken) + { + var hasAnyOutData = HasAValueReturn(methodSymbol) || HasAnOutOrRefParam(methodSymbol); + if (!hasAnyOutData) + { + // With no out data, there's nothing to do here + return; + } + + // TODO: Use DFA to find meaningful returns? https://github.com/dotnet/roslyn-analyzers/blob/9e5f533cbafcc5579e4d758bc9bde27b7611ca54/docs/Writing%20dataflow%20analysis%20based%20analyzers.md + if (HasAValueReturn(methodSymbol)) + { + foreach (var location in methodSymbol.GetDefinitionLocationsToShow()) + { + if (location.SourceTree is null) + { + continue; + } + + var node = location.FindNode(cancellationToken); + var sourceDoc = collector.Solution.GetRequiredDocument(location.SourceTree); + var syntaxFacts = sourceDoc.GetRequiredLanguageService(); + var semanticModel = await sourceDoc.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + var operation = semanticModel.GetOperation(node, cancellationToken); + + // In VB the parent node contains the operation (IBlockOperation) instead of the one returned + // by the symbol location. + if (operation is null && node.Parent is not null) + { + operation = semanticModel.GetOperation(node.Parent, cancellationToken); + } + + if (operation is null) + { + continue; + } + + await collector.VisitAsync(operation, cancellationToken).ConfigureAwait(false); + } + } + + if (HasAnOutOrRefParam(methodSymbol)) + { + foreach (var outOrRefParam in methodSymbol.Parameters.Where(p => p.IsRefOrOut())) + { + if (!outOrRefParam.IsFromSource()) + { + continue; + } + + await TrackVariableReferencesAsync(outOrRefParam, collector, cancellationToken).ConfigureAwait(false); + } + } + + // TODO check for Task + static bool HasAValueReturn(IMethodSymbol methodSymbol) + => methodSymbol.ReturnType.SpecialType != SpecialType.System_Void; + + static bool HasAnOutOrRefParam(IMethodSymbol methodSymbol) + => methodSymbol.Parameters.Any(p => p.IsRefOrOut()); + } + + private static async Task<(ISymbol?, SyntaxNode?)> GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) + { + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var selectedNode = root.FindNode(textSpan); + + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var selectedSymbol = + semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol + ?? semanticModel.GetDeclaredSymbol(selectedNode, cancellationToken); + + if (selectedSymbol is null) + { + var syntaxFacts = document.GetRequiredLanguageService(); + + // If the node is an argument it's possible that it's just + // an identifier in the expression. If so, then grab the symbol + // for that node instead of the argument. + // EX: MyMethodCall($$x, y) should get the identifier x and + // the symbol for that identifier + if (syntaxFacts.IsArgument(selectedNode)) + { + selectedNode = syntaxFacts.GetExpressionOfArgument(selectedNode)!; + selectedSymbol = semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol; + } + } + + return (selectedSymbol, selectedNode); + } + + private static async Task TrackVariableDefinitionsAsync(ISymbol symbol, OperationCollector collector, CancellationToken cancellationToken) + { + foreach (var definitionLocation in symbol.Locations) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (definitionLocation is not { SourceTree: not null }) + { + continue; + } + + var node = definitionLocation.FindNode(cancellationToken); + var document = collector.Solution.GetRequiredDocument(node.SyntaxTree); + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + var operation = semanticModel.GetOperation(node, cancellationToken); + + var declarators = operation switch + { + IVariableDeclaratorOperation variableDeclarator => ImmutableArray.Create(variableDeclarator), + IVariableDeclarationOperation variableDeclaration => variableDeclaration.Declarators, + _ => ImmutableArray.Empty + }; + + foreach (var declarator in declarators) + { + var initializer = declarator.GetVariableInitializer(); + if (initializer is null) + { + continue; + } + + await collector.VisitAsync(initializer, cancellationToken).ConfigureAwait(false); + } + } + } + } +} diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 5df16af3ae4df..b302a53c9677e 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -35,7 +35,7 @@ public async Task> TrackValueSourceAsync( { using var logger = Logger.LogBlock(FunctionId.ValueTracking_TrackValueSource, cancellationToken, LogLevel.Information); var progressTracker = new ValueTrackingProgressCollector(); - await TrackValueSourceInternalAsync(selection, document, progressTracker, cancellationToken).ConfigureAwait(false); + await ValueTracker.TrackValueSourceInternalAsync(selection, document, progressTracker, cancellationToken).ConfigureAwait(false); return progressTracker.GetItems(); } @@ -46,74 +46,20 @@ public async Task TrackValueSourceAsync( CancellationToken cancellationToken) { using var logger = Logger.LogBlock(FunctionId.ValueTracking_TrackValueSource, cancellationToken, LogLevel.Information); - await TrackValueSourceInternalAsync( + await ValueTracker.TrackValueSourceInternalAsync( selection, document, progressCollector, cancellationToken).ConfigureAwait(false); } - private static async Task TrackValueSourceInternalAsync( - TextSpan selection, - Document document, - ValueTrackingProgressCollector progressCollector, - CancellationToken cancellationToken) - { - var (symbol, node) = await GetSelectedSymbolAsync(selection, document, cancellationToken).ConfigureAwait(false); - var operationCollector = new OperationCollector(progressCollector, document.Project.Solution); - - if (symbol - is IPropertySymbol - or IFieldSymbol - or ILocalSymbol - or IParameterSymbol) - { - RoslynDebug.AssertNotNull(node); - - var solution = document.Project.Solution; - var declaringSyntaxReferences = symbol.DeclaringSyntaxReferences; - var syntaxFacts = document.GetRequiredLanguageService(); - - // If the selection is within a declaration of the symbol, we want to include - // all declarations and assignments of the symbol - if (declaringSyntaxReferences.Any(r => r.Span.IntersectsWith(selection))) - { - // Add all initializations of the symbol. Those are not caught in - // the reference finder but should still show up in the tree - foreach (var syntaxRef in declaringSyntaxReferences) - { - var location = Location.Create(syntaxRef.SyntaxTree, syntaxRef.Span); - await progressCollector.TryReportAsync(solution, location, symbol, cancellationToken).ConfigureAwait(false); - } - - await TrackVariableReferencesAsync(symbol, operationCollector, cancellationToken).ConfigureAwait(false); - } - // The selection is not on a declaration, check that the node - // is on the left side of an assignment. If so, populate so we can - // track the RHS values that contribute to this value - else if (syntaxFacts.IsLeftSideOfAnyAssignment(node)) - { - await AddItemsFromAssignmentAsync(document, node, operationCollector, cancellationToken).ConfigureAwait(false); - } - // Not on the left part of an assignment? Then just add an item with the statement - // and the symbol. It should be the top item, and children will find the sources - // of the value. A good example is a return statement, such as "return $$x", - // where $$ is the cursor position. The top item should have the return statement for - // context, and the remaining items should expand into the assignments of x - else - { - await progressCollector.TryReportAsync(document.Project.Solution, node.GetLocation(), symbol, cancellationToken).ConfigureAwait(false); - } - } - } - public async Task> TrackValueSourceAsync( ValueTrackedItem previousTrackedItem, CancellationToken cancellationToken) { using var logger = Logger.LogBlock(FunctionId.ValueTracking_TrackValueSource, cancellationToken, LogLevel.Information); var progressTracker = new ValueTrackingProgressCollector(); - await TrackValueSourceInternalAsync(previousTrackedItem, progressTracker, cancellationToken).ConfigureAwait(false); + await ValueTracker.TrackValueSourceInternalAsync(previousTrackedItem, progressTracker, cancellationToken).ConfigureAwait(false); return progressTracker.GetItems(); } @@ -123,249 +69,10 @@ public async Task TrackValueSourceAsync( CancellationToken cancellationToken) { using var logger = Logger.LogBlock(FunctionId.ValueTracking_TrackValueSource, cancellationToken, LogLevel.Information); - await TrackValueSourceInternalAsync( + await ValueTracker.TrackValueSourceInternalAsync( previousTrackedItem, progressCollector, cancellationToken).ConfigureAwait(false); } - - private static async Task TrackValueSourceInternalAsync( - ValueTrackedItem previousTrackedItem, - ValueTrackingProgressCollector progressCollector, - CancellationToken cancellationToken) - { - progressCollector.Parent = previousTrackedItem; - var operationCollector = new OperationCollector(progressCollector, previousTrackedItem.Document.Project.Solution); - - switch (previousTrackedItem.Symbol) - { - case ILocalSymbol: - case IPropertySymbol: - case IFieldSymbol: - { - // The "output" is a variable assignment, track places where it gets assigned and defined - await TrackVariableDefinitionsAsync(previousTrackedItem.Symbol, operationCollector, cancellationToken).ConfigureAwait(false); - await TrackVariableReferencesAsync(previousTrackedItem.Symbol, operationCollector, cancellationToken).ConfigureAwait(false); - } - break; - - case IParameterSymbol parameterSymbol: - { - var previousSymbol = previousTrackedItem.Parent?.Symbol; - - // If the current parameter is a parameter symbol for the previous tracked method it should be treated differently. - // For example: - // string PrependString(string pre, string s) => pre + s; - // ^--- previously tracked ^---- current parameter being tracked - // - // In this case, s is being tracked because it contributed to the return of the method. We only - // want to track assignments to s that could impact the return rather than tracking the same method - // twice. - var isParameterForPreviousTrackedMethod = previousSymbol?.Equals(parameterSymbol.ContainingSymbol) == true; - - // For Ref or Out parameters, they contribute data across method calls through assignments - // within the method. No need to track returns - // Ex: TryGetValue("mykey", out var [|v|]) - // [|v|] is the interesting part, we don't care what the method returns - var isRefOrOut = parameterSymbol.IsRefOrOut(); - - // Always track the parameter assignments as variables, in case they are assigned anywhere in the method - await TrackVariableReferencesAsync(parameterSymbol, operationCollector, cancellationToken).ConfigureAwait(false); - - var trackMethod = !(isParameterForPreviousTrackedMethod || isRefOrOut); - if (trackMethod) - { - await TrackParameterSymbolAsync(parameterSymbol, operationCollector, cancellationToken).ConfigureAwait(false); - } - } - break; - - case IMethodSymbol methodSymbol: - { - // The "output" is from a method, meaning it has a return or out param that is used. Track those - await TrackMethodSymbolAsync(methodSymbol, operationCollector, cancellationToken).ConfigureAwait(false); - } - break; - } - } - - private static async Task TrackVariableDefinitionsAsync(ISymbol symbol, OperationCollector collector, CancellationToken cancellationToken) - { - foreach (var definitionLocation in symbol.Locations) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (definitionLocation is not { SourceTree: not null }) - { - continue; - } - - var node = definitionLocation.FindNode(cancellationToken); - var document = collector.Solution.GetRequiredDocument(node.SyntaxTree); - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - - var operation = semanticModel.GetOperation(node, cancellationToken); - - var declarators = operation switch - { - IVariableDeclaratorOperation variableDeclarator => ImmutableArray.Create(variableDeclarator), - IVariableDeclarationOperation variableDeclaration => variableDeclaration.Declarators, - _ => ImmutableArray.Empty - }; - - foreach (var declarator in declarators) - { - var initializer = declarator.GetVariableInitializer(); - if (initializer is null) - { - continue; - } - - await collector.VisitAsync(initializer, cancellationToken).ConfigureAwait(false); - } - } - } - - private static async Task TrackVariableReferencesAsync(ISymbol symbol, OperationCollector collector, CancellationToken cancellationToken) - { - var findReferenceProgressCollector = new FindReferencesProgress(collector); - await SymbolFinder.FindReferencesAsync( - symbol, - collector.Solution, - findReferenceProgressCollector, - documents: null, FindReferencesSearchOptions.Default, cancellationToken).ConfigureAwait(false); - } - - private static async Task TrackParameterSymbolAsync( - IParameterSymbol parameterSymbol, - OperationCollector collector, - CancellationToken cancellationToken) - { - var containingMethod = (IMethodSymbol)parameterSymbol.ContainingSymbol; - var findReferenceProgressCollector = new FindReferencesProgress(collector); - await SymbolFinder.FindReferencesAsync( - containingMethod, - collector.Solution, - findReferenceProgressCollector, - documents: null, FindReferencesSearchOptions.Default, cancellationToken).ConfigureAwait(false); - } - - private static async Task TrackMethodSymbolAsync(IMethodSymbol methodSymbol, OperationCollector collector, CancellationToken cancellationToken) - { - var hasAnyOutData = HasAValueReturn(methodSymbol) || HasAnOutOrRefParam(methodSymbol); - if (!hasAnyOutData) - { - // With no out data, there's nothing to do here - return; - } - - // TODO: Use DFA to find meaningful returns? https://github.com/dotnet/roslyn-analyzers/blob/9e5f533cbafcc5579e4d758bc9bde27b7611ca54/docs/Writing%20dataflow%20analysis%20based%20analyzers.md - if (HasAValueReturn(methodSymbol)) - { - foreach (var location in methodSymbol.GetDefinitionLocationsToShow()) - { - if (location.SourceTree is null) - { - continue; - } - - var node = location.FindNode(cancellationToken); - var sourceDoc = collector.Solution.GetRequiredDocument(location.SourceTree); - var syntaxFacts = sourceDoc.GetRequiredLanguageService(); - var semanticModel = await sourceDoc.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - - var operation = semanticModel.GetOperation(node, cancellationToken); - - // In VB the parent node contains the operation (IBlockOperation) instead of the one returned - // by the symbol location. - if (operation is null && node.Parent is not null) - { - operation = semanticModel.GetOperation(node.Parent, cancellationToken); - } - - if (operation is null) - { - continue; - } - - await collector.VisitAsync(operation, cancellationToken).ConfigureAwait(false); - } - } - - if (HasAnOutOrRefParam(methodSymbol)) - { - foreach (var outOrRefParam in methodSymbol.Parameters.Where(p => p.IsRefOrOut())) - { - if (!outOrRefParam.IsFromSource()) - { - continue; - } - - await TrackVariableReferencesAsync(outOrRefParam, collector, cancellationToken).ConfigureAwait(false); - } - } - - // TODO check for Task - static bool HasAValueReturn(IMethodSymbol methodSymbol) - => methodSymbol.ReturnType.SpecialType != SpecialType.System_Void; - - static bool HasAnOutOrRefParam(IMethodSymbol methodSymbol) - => methodSymbol.Parameters.Any(p => p.IsRefOrOut()); - } - - private static async Task<(ISymbol?, SyntaxNode?)> GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) - { - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var selectedNode = root.FindNode(textSpan); - - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var selectedSymbol = - semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol - ?? semanticModel.GetDeclaredSymbol(selectedNode, cancellationToken); - - if (selectedSymbol is null) - { - var syntaxFacts = document.GetRequiredLanguageService(); - - // If the node is an argument it's possible that it's just - // an identifier in the expression. If so, then grab the symbol - // for that node instead of the argument. - // EX: MyMethodCall($$x, y) should get the identifier x and - // the symbol for that identifier - if (syntaxFacts.IsArgument(selectedNode)) - { - selectedNode = syntaxFacts.GetExpressionOfArgument(selectedNode)!; - selectedSymbol = semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol; - } - } - - return (selectedSymbol, selectedNode); - } - - private static async Task AddItemsFromAssignmentAsync(Document document, SyntaxNode lhsNode, OperationCollector collector, CancellationToken cancellationToken) - { - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var operation = semanticModel.GetOperation(lhsNode, cancellationToken); - if (operation is null) - { - return; - } - - IAssignmentOperation? assignmentOperation = null; - - while (assignmentOperation is null - && operation is not null) - { - assignmentOperation = operation as IAssignmentOperation; - operation = operation.Parent; - } - - if (assignmentOperation is null) - { - return; - } - - await collector.VisitAsync(assignmentOperation, cancellationToken).ConfigureAwait(false); - } } } diff --git a/src/Workspaces/Remote/ServiceHub/Services/ValueTracking/RemoteValueTrackingService.cs b/src/Workspaces/Remote/ServiceHub/Services/ValueTracking/RemoteValueTrackingService.cs new file mode 100644 index 0000000000000..7099c762a4453 --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Services/ValueTracking/RemoteValueTrackingService.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.CodeAnalysis.Remote.Services.ValueTracking +{ + class RemoteValueTrackingService + { + } +} From 714368f702a75bc82c9e123a49d9240a10862646 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Mon, 10 May 2021 12:20:09 -0700 Subject: [PATCH 071/127] Rename to TreeItemViewModel --- .../ValueTracking/{TreeViewItem.cs => TreeItemViewModel.cs} | 6 +++--- .../Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs | 4 ++-- .../Core/Def/ValueTracking/ValueTrackingCommandHandler.cs | 4 ++-- .../Core/Def/ValueTracking/ValueTrackingToolWindow.cs | 2 +- .../Core/Def/ValueTracking/ValueTrackingTree.xaml | 2 +- .../Core/Def/ValueTracking/ValueTrackingTree.xaml.cs | 2 +- .../Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs | 6 +++--- 7 files changed, 13 insertions(+), 13 deletions(-) rename src/VisualStudio/Core/Def/ValueTracking/{TreeViewItem.cs => TreeItemViewModel.cs} (97%) diff --git a/src/VisualStudio/Core/Def/ValueTracking/TreeViewItem.cs b/src/VisualStudio/Core/Def/ValueTracking/TreeItemViewModel.cs similarity index 97% rename from src/VisualStudio/Core/Def/ValueTracking/TreeViewItem.cs rename to src/VisualStudio/Core/Def/ValueTracking/TreeItemViewModel.cs index 480e75e00aaa5..dff6cef652821 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/TreeViewItem.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/TreeItemViewModel.cs @@ -20,7 +20,7 @@ namespace Microsoft.VisualStudio.LanguageServices.ValueTracking { - internal class TreeViewItem : TreeViewItemBase + internal class TreeItemViewModel : TreeViewItemBase { private readonly SourceText _sourceText; private readonly ISymbol _symbol; @@ -75,7 +75,7 @@ public IList Inlines } } - public TreeViewItem( + public TreeItemViewModel( Document document, TextSpan textSpan, SourceText sourceText, @@ -84,7 +84,7 @@ public TreeViewItem( ValueTrackingTreeViewModel treeViewModel, IGlyphService glyphService, IThreadingContext threadingContext, - ImmutableArray children = default) + ImmutableArray children = default) { Document = document; TextSpan = textSpan; diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs index 1289f429b5f8c..d622e767b9377 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs @@ -13,7 +13,7 @@ namespace Microsoft.VisualStudio.LanguageServices.ValueTracking { - internal class ValueTrackedTreeItemViewModel : TreeViewItem + internal class ValueTrackedTreeItemViewModel : TreeItemViewModel { private bool _childrenCalculated; private readonly Solution _solution; @@ -38,7 +38,7 @@ public ValueTrackedTreeItemViewModel( IGlyphService glyphService, IValueTrackingService valueTrackingService, IThreadingContext threadingContext, - ImmutableArray children = default) + ImmutableArray children = default) : base( trackedItem.Document, trackedItem.Span, diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs index ec76feedce86a..1e7f9f88854d4 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs @@ -163,7 +163,7 @@ private async Task ShowToolWindowAsync(ITextView textView, ISymbol selectedSymbo var documentSpan = await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync(document, location.SourceSpan, cancellationToken).ConfigureAwait(false); var classificationResult = await ClassifiedSpansAndHighlightSpanFactory.ClassifyAsync(documentSpan, cancellationToken).ConfigureAwait(false); - var root = new TreeViewItem( + var root = new TreeItemViewModel( document, location.SourceSpan, sourceText, @@ -181,7 +181,7 @@ private async Task ShowToolWindowAsync(ITextView textView, ISymbol selectedSymbo await ShowToolWindowAsync(cancellationToken).ConfigureAwait(true); - TreeViewItem CreateViewModel(ValueTrackedItem valueTrackedItem, ImmutableArray children = default) + TreeItemViewModel CreateViewModel(ValueTrackedItem valueTrackedItem, ImmutableArray children = default) => new ValueTrackedTreeItemViewModel( valueTrackedItem, solution, diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs index d8f5a36446a36..35e8fe5ab766b 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs @@ -60,7 +60,7 @@ public ValueTrackingToolWindow(ValueTrackingTreeViewModel viewModel) ViewModel = viewModel; } - public TreeViewItem? Root + public TreeItemViewModel? Root { get => ViewModel?.Roots.Single(); set diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml index 6b2037a95d8c0..0e7efe72d182e 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml @@ -47,7 +47,7 @@ - + diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs index 93d3f87488af5..6c6c403c92b62 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTree.xaml.cs @@ -34,7 +34,7 @@ public ValueTrackingTree(ValueTrackingTreeViewModel viewModel) private void ValueTrackingTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs e) { - _viewModel.SelectedItem = (TreeViewItem)e.NewValue; + _viewModel.SelectedItem = (TreeItemViewModel)e.NewValue; } } } diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs index 80f908a932269..1caf332ea603b 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeViewModel.cs @@ -25,10 +25,10 @@ public Brush? HighlightBrush public IClassificationFormatMap ClassificationFormatMap { get; } public ClassificationTypeMap ClassificationTypeMap { get; } public IEditorFormatMapService FormatMapService { get; } - public ObservableCollection Roots { get; } = new(); + public ObservableCollection Roots { get; } = new(); - private TreeViewItem? _selectedItem; - public TreeViewItem? SelectedItem + private TreeItemViewModel? _selectedItem; + public TreeItemViewModel? SelectedItem { get => _selectedItem; set => SetProperty(ref _selectedItem, value); From 0d519f96bb5ae1fba2e7f029f3eeed6a01163955 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Mon, 10 May 2021 15:36:03 -0700 Subject: [PATCH 072/127] Follow-up on merge --- .../CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs index bbf49a491eb56..377645bb42318 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs @@ -383,7 +383,7 @@ Cci.ITypeReference Cci.ITypeDefinition.GetBaseClass(EmitContext context) foreach ((MethodSymbol body, MethodSymbol implemented) in container.GetSynthesizedExplicitImplementations(cancellationToken: default).MethodImpls) { Debug.Assert(body.ContainingType == (object)container); - yield return new Microsoft.Cci.MethodImplementation(body.GetCciAdapter(), moduleBeingBuilt.TranslateOverriddenMethodReference(implemented, (CSharpSyntaxNode)context.SyntaxNodeOpt, context.Diagnostics)); + yield return new Microsoft.Cci.MethodImplementation(body.GetCciAdapter(), moduleBeingBuilt.TranslateOverriddenMethodReference(implemented, (CSharpSyntaxNode)context.SyntaxNode, context.Diagnostics)); } } From 84acaf532671598d2b49f9d2f39dfecbc00385d4 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Tue, 11 May 2021 16:37:01 -0700 Subject: [PATCH 073/127] Add support for implementations of interface static abstract properties/events in classes and structures (#53265) --- .../CSharp/Portable/Parser/LanguageParser.cs | 2 +- .../Symbols/Metadata/PE/PEEventSymbol.cs | 6 +- .../Portable/Symbols/Source/ModifierUtils.cs | 15 + .../Source/SourceEventAccessorSymbol.cs | 2 +- .../Symbols/Source/SourceEventSymbol.cs | 31 +- .../Source/SourceOrdinaryMethodSymbolBase.cs | 10 +- .../Source/SourcePropertyAccessorSymbol.cs | 4 +- .../Symbols/Source/SourcePropertySymbol.cs | 14 +- .../SourceUserDefinedOperatorSymbolBase.cs | 4 +- .../CSharp/Portable/Symbols/TypeSymbol.cs | 32 +- .../Semantics/InheritanceBindingTests.cs | 22 +- .../DefaultInterfaceImplementationTests.cs | 12 + .../StaticAbstractMembersInInterfacesTests.cs | 5523 ++++++++++++++++- 13 files changed, 5413 insertions(+), 264 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 227c219e5afb2..eb061d4e2014f 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -2375,7 +2375,7 @@ private MemberDeclarationSyntax ParseMemberDeclarationOrStatementCore(SyntaxKind try { - // Try as a regualr statement rather than a member declaration, if appropriate. + // Try as a regular statement rather than a member declaration, if appropriate. if ((!haveAttributes || !IsScript) && !haveModifiers && (type.Kind == SyntaxKind.RefType || !IsOperatorStart(out _, advanceParser: false))) { this.Reset(ref afterAttributesPoint); diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEEventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEEventSymbol.cs index 0b4736a519dfb..802b89f730d4f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEEventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEEventSymbol.cs @@ -381,7 +381,11 @@ public override ImmutableArray ExplicitInterfaceImplementations } var implementedEvents = PEPropertyOrEventHelpers.GetEventsForExplicitlyImplementedAccessor(_addMethod); - implementedEvents.IntersectWith(PEPropertyOrEventHelpers.GetEventsForExplicitlyImplementedAccessor(_removeMethod)); + + if (implementedEvents.Count != 0) + { + implementedEvents.IntersectWith(PEPropertyOrEventHelpers.GetEventsForExplicitlyImplementedAccessor(_removeMethod)); + } var builder = ArrayBuilder.GetInstance(); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs index b4aa7247ea04c..9ad15ed20e0e8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs @@ -171,6 +171,21 @@ internal static void ReportUnsupportedModifiersForLanguageVersion(DeclarationMod } } + internal static void CheckFeatureAvailabilityForStaticAbstractMembersInInterfacesIfNeeded(DeclarationModifiers mods, bool isExplicitInterfaceImplementation, Location location, BindingDiagnosticBag diagnostics) + { + if (isExplicitInterfaceImplementation && (mods & DeclarationModifiers.Static) != 0) + { + Debug.Assert(location.SourceTree is not null); + + LanguageVersion availableVersion = ((CSharpParseOptions)location.SourceTree.Options).LanguageVersion; + LanguageVersion requiredVersion = MessageID.IDS_FeatureStaticAbstractMembersInInterfaces.RequiredVersion(); + if (availableVersion < requiredVersion) + { + ModifierUtils.ReportUnsupportedModifiersForLanguageVersion(mods, DeclarationModifiers.Static, location, diagnostics, availableVersion, requiredVersion); + } + } + } + internal static DeclarationModifiers AdjustModifiersForAnInterfaceMember(DeclarationModifiers mods, bool hasBody, bool isExplicitInterfaceImplementation) { if (isExplicitInterfaceImplementation) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs index a58e26f6b3bcc..03079cdc7b9b4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs @@ -58,7 +58,7 @@ public SourceEventAccessorSymbol( returnsVoid: false, // until we learn otherwise (in LazyMethodChecks). isExtensionMethod: false, isNullableAnalysisEnabled: isNullableAnalysisEnabled, - isMetadataVirtualIgnoringModifiers: @event.IsExplicitInterfaceImplementation); + isMetadataVirtualIgnoringModifiers: @event.IsExplicitInterfaceImplementation && (@event.Modifiers & DeclarationModifiers.Static) == 0); _name = GetOverriddenAccessorName(@event, isAdder) ?? name; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs index e6e018667b044..87b77df7f5c2c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Emit; +using System.Linq; namespace Microsoft.CodeAnalysis.CSharp.Symbols { @@ -468,10 +469,18 @@ private DeclarationModifiers MakeModifiers(SyntaxTokenList modifiers, bool expli DeclarationModifiers.AccessibilityMask; } } - else if (isInterface) + else { Debug.Assert(explicitInterfaceImplementation); - allowedModifiers |= DeclarationModifiers.Abstract; + + if (isInterface) + { + allowedModifiers |= DeclarationModifiers.Abstract; + } + else + { + allowedModifiers |= DeclarationModifiers.Static; + } } if (this.ContainingType.IsStructType()) @@ -486,6 +495,8 @@ private DeclarationModifiers MakeModifiers(SyntaxTokenList modifiers, bool expli var mods = ModifierUtils.MakeAndCheckNontypeMemberModifiers(modifiers, defaultAccess, allowedModifiers, location, diagnostics, out modifierErrors); + ModifierUtils.CheckFeatureAvailabilityForStaticAbstractMembersInInterfacesIfNeeded(mods, explicitInterfaceImplementation, location, diagnostics); + this.CheckUnsafeModifier(mods, diagnostics); ModifierUtils.ReportDefaultInterfaceImplementationModifiers(!isFieldLike, mods, @@ -748,6 +759,22 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, { compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); } + + EventSymbol? explicitlyImplementedEvent = ExplicitInterfaceImplementations.FirstOrDefault(); + + if (explicitlyImplementedEvent is object) + { + CheckExplicitImplementationAccessor(AddMethod, explicitlyImplementedEvent.AddMethod, explicitlyImplementedEvent, diagnostics); + CheckExplicitImplementationAccessor(RemoveMethod, explicitlyImplementedEvent.RemoveMethod, explicitlyImplementedEvent, diagnostics); + } + } + + private void CheckExplicitImplementationAccessor(MethodSymbol? thisAccessor, MethodSymbol? otherAccessor, EventSymbol explicitlyImplementedEvent, BindingDiagnosticBag diagnostics) + { + if (!otherAccessor.IsImplementable() && thisAccessor is object) + { + diagnostics.Add(ErrorCode.ERR_ExplicitPropertyAddingAccessor, thisAccessor.Locations[0], thisAccessor, explicitlyImplementedEvent); + } } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs index 270c158ef8ab7..a122f3ff342e0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs @@ -276,15 +276,7 @@ public override string Name hasExplicitAccessMod = true; } - if (isExplicitInterfaceImplementation && (mods & DeclarationModifiers.Static) != 0) - { - LanguageVersion availableVersion = ((CSharpParseOptions)location.SourceTree.Options).LanguageVersion; - LanguageVersion requiredVersion = MessageID.IDS_FeatureStaticAbstractMembersInInterfaces.RequiredVersion(); - if (availableVersion < requiredVersion) - { - ModifierUtils.ReportUnsupportedModifiersForLanguageVersion(mods, DeclarationModifiers.Static, location, diagnostics, availableVersion, requiredVersion); - } - } + ModifierUtils.CheckFeatureAvailabilityForStaticAbstractMembersInInterfacesIfNeeded(mods, isExplicitInterfaceImplementation, location, diagnostics); this.CheckUnsafeModifier(mods, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs index 66e950cdf97fb..474e904934cdb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs @@ -160,7 +160,7 @@ private SourcePropertyAccessorSymbol( // ReturnsVoid property is overridden in this class so // returnsVoid argument to MakeFlags is ignored. this.MakeFlags(MethodKind.PropertyGet, declarationModifiers, returnsVoid: false, isExtensionMethod: false, isNullableAnalysisEnabled: isNullableAnalysisEnabled, - isMetadataVirtualIgnoringModifiers: property.IsExplicitInterfaceImplementation); + isMetadataVirtualIgnoringModifiers: property.IsExplicitInterfaceImplementation && (declarationModifiers & DeclarationModifiers.Static) == 0); CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasBody: true, diagnostics: diagnostics); CheckModifiersForBody(location, diagnostics); @@ -219,7 +219,7 @@ protected SourcePropertyAccessorSymbol( // ReturnsVoid property is overridden in this class so // returnsVoid argument to MakeFlags is ignored. this.MakeFlags(methodKind, declarationModifiers, returnsVoid: false, isExtensionMethod: false, isNullableAnalysisEnabled: isNullableAnalysisEnabled, - isMetadataVirtualIgnoringModifiers: property.IsExplicitInterfaceImplementation); + isMetadataVirtualIgnoringModifiers: property.IsExplicitInterfaceImplementation && (declarationModifiers & DeclarationModifiers.Static) == 0); CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasBody: hasBody || hasExpressionBody || isAutoPropertyAccessor, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index ca475dbb3a0a7..f71c881683cec 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -307,10 +307,18 @@ private static DeclarationModifiers MakeModifiers( DeclarationModifiers.AccessibilityMask; } } - else if (isInterface) + else { Debug.Assert(isExplicitInterfaceImplementation); - allowedModifiers |= DeclarationModifiers.Abstract; + + if (isInterface) + { + allowedModifiers |= DeclarationModifiers.Abstract; + } + else if (!isIndexer) + { + allowedModifiers |= DeclarationModifiers.Static; + } } if (containingType.IsStructType()) @@ -322,6 +330,8 @@ private static DeclarationModifiers MakeModifiers( var mods = ModifierUtils.MakeAndCheckNontypeMemberModifiers(modifiers, defaultAccess, allowedModifiers, location, diagnostics, out modifierErrors); + ModifierUtils.CheckFeatureAvailabilityForStaticAbstractMembersInInterfacesIfNeeded(mods, isExplicitInterfaceImplementation, location, diagnostics); + containingType.CheckUnsafeModifier(mods, location, diagnostics); ModifierUtils.ReportDefaultInterfaceImplementationModifiers(accessorsHaveImplementation, mods, diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs index 6bbc3653c2bce..ff60194e421ea 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs @@ -273,7 +273,9 @@ protected sealed override MethodSymbol FindExplicitlyImplementedMethod(BindingDi return null; } - protected sealed override TypeSymbol ExplicitInterfaceType => _explicitInterfaceType; +#nullable enable + protected sealed override TypeSymbol? ExplicitInterfaceType => _explicitInterfaceType; +#nullable disable private void CheckValueParameters(BindingDiagnosticBag diagnostics) { diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs index 1785d34b06704..0d4ccc122bc2e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs @@ -865,7 +865,10 @@ private static Symbol ComputeImplementationForInterfaceMember(Symbol interfaceMe return null; } - if (((object)currType != implementingType || !currType.IsDefinition) && interfaceMember is MethodSymbol interfaceMethod) + bool checkPendingExplicitImplementations = ((object)currType != implementingType || !currType.IsDefinition); + + if (checkPendingExplicitImplementations && interfaceMember is MethodSymbol interfaceMethod && + currType.InterfacesAndTheirBaseInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo).ContainsKey(interfaceType)) { // Check for implementations that are going to be explicit once types are emitted MethodSymbol bodyOfSynthesizedMethodImpl = currType.GetBodyOfSynthesizedInterfaceMethodImpl(interfaceMethod); @@ -877,7 +880,7 @@ private static Symbol ComputeImplementationForInterfaceMember(Symbol interfaceMe } } - if (IsExplicitlyImplementedViaAccessors(interfaceMember, currType, out Symbol currTypeExplicitImpl)) + if (IsExplicitlyImplementedViaAccessors(checkPendingExplicitImplementations, interfaceMember, currType, ref useSiteInfo, out Symbol currTypeExplicitImpl)) { // We are looking for a property or event implementation and found an explicit implementation // for its accessor(s) in this type. Stop the process and return event/property associated @@ -1435,15 +1438,15 @@ private static (MethodSymbol interfaceAccessor1, MethodSymbol interfaceAccessor2 /// even if it is null. That is, never attempt to find an implicit implementation for an interface property or event with an /// explicitly implemented accessor. /// - private static bool IsExplicitlyImplementedViaAccessors(Symbol interfaceMember, TypeSymbol currType, out Symbol implementingMember) + private static bool IsExplicitlyImplementedViaAccessors(bool checkPendingExplicitImplementations, Symbol interfaceMember, TypeSymbol currType, ref CompoundUseSiteInfo useSiteInfo, out Symbol implementingMember) { (MethodSymbol interfaceAccessor1, MethodSymbol interfaceAccessor2) = GetImplementableAccessors(interfaceMember); Symbol associated1; Symbol associated2; - if (TryGetExplicitImplementationAssociatedPropertyOrEvent(interfaceAccessor1, currType, out associated1) | // NB: not || - TryGetExplicitImplementationAssociatedPropertyOrEvent(interfaceAccessor2, currType, out associated2)) + if (TryGetExplicitImplementationAssociatedPropertyOrEvent(checkPendingExplicitImplementations, interfaceAccessor1, currType, ref useSiteInfo, out associated1) | // NB: not || + TryGetExplicitImplementationAssociatedPropertyOrEvent(checkPendingExplicitImplementations, interfaceAccessor2, currType, ref useSiteInfo, out associated2)) { // If there's more than one associated property/event, don't do anything special - just let the algorithm // fail in the usual way. @@ -1455,7 +1458,7 @@ private static bool IsExplicitlyImplementedViaAccessors(Symbol interfaceMember, // If we haven't then there is no implementation. We need this check to match dev11 in some edge cases // (e.g. IndexerTests.AmbiguousExplicitIndexerImplementation). Such cases already fail // to roundtrip correctly, so it's not important to check for a particular compilation. - if ((object)implementingMember != null && implementingMember.OriginalDefinition.ContainingModule is not PEModuleSymbol) + if ((object)implementingMember != null && implementingMember.OriginalDefinition.ContainingModule is not PEModuleSymbol && implementingMember.IsExplicitInterfaceImplementation()) { implementingMember = null; } @@ -1472,7 +1475,7 @@ private static bool IsExplicitlyImplementedViaAccessors(Symbol interfaceMember, return false; } - private static bool TryGetExplicitImplementationAssociatedPropertyOrEvent(MethodSymbol interfaceAccessor, TypeSymbol currType, out Symbol associated) + private static bool TryGetExplicitImplementationAssociatedPropertyOrEvent(bool checkPendingExplicitImplementations, MethodSymbol interfaceAccessor, TypeSymbol currType, ref CompoundUseSiteInfo useSiteInfo, out Symbol associated) { if ((object)interfaceAccessor != null) { @@ -1487,6 +1490,19 @@ private static bool TryGetExplicitImplementationAssociatedPropertyOrEvent(Method : null; return true; } + + if (checkPendingExplicitImplementations && + currType.InterfacesAndTheirBaseInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo).ContainsKey(interfaceAccessor.ContainingType)) + { + // Check for implementations that are going to be explicit once types are emitted + MethodSymbol bodyOfSynthesizedMethodImpl = currType.GetBodyOfSynthesizedInterfaceMethodImpl(interfaceAccessor); + + if (bodyOfSynthesizedMethodImpl is object) + { + associated = bodyOfSynthesizedMethodImpl.AssociatedSymbol; + return true; + } + } } associated = null; @@ -1674,6 +1690,8 @@ private static void ReportImplicitImplementationMatchDiagnostics(Symbol interfac if (member.DeclaredAccessibility != Accessibility.Public || member.IsStatic || member == implicitImpl) { //do nothing - not an ambiguous implementation + + // PROTOTYPE(StaticAbstractMembersInInterfaces): We likely need to do something here for static members. } else if (MemberSignatureComparer.RuntimeImplicitImplementationComparer.Equals(interfaceMember, member) && !member.IsAccessor()) { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs index 42fb8ad138020..8001df4c093f1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs @@ -181,8 +181,15 @@ static int IGoo.Property12 { set { } } Diagnostic(ErrorCode.ERR_BadMemberFlag, "Property9").WithArguments("protected internal"), // (32,22): error CS0106: The modifier 'private' is not valid for this item Diagnostic(ErrorCode.ERR_BadMemberFlag, "Property10").WithArguments("private"), - // (35,21): error CS0106: The modifier 'static' is not valid for this item - Diagnostic(ErrorCode.ERR_BadMemberFlag, "Property12").WithArguments("static"), + // (35,21): error CS8703: The modifier 'static' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // static int IGoo.Property12 { set { } } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "Property12").WithArguments("static", "9.0", "preview").WithLocation(35, 21), + // (35,21): error CS0539: 'AbstractGoo.Property12' in explicit interface declaration is not found among members of the interface that can be implemented + // static int IGoo.Property12 { set { } } + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "Property12").WithArguments("AbstractGoo.Property12").WithLocation(35, 21), + // (18,30): error CS0535: 'AbstractGoo' does not implement interface member 'IGoo.Property12' + // abstract class AbstractGoo : IGoo + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "IGoo").WithArguments("AbstractGoo", "IGoo.Property12").WithLocation(18, 30), // (34,34): warning CS0626: Method, operator, or accessor 'AbstractGoo.IGoo.Property11.set' is marked external and has no attributes on it. Consider adding a DllImport attribute to specify the external implementation. Diagnostic(ErrorCode.WRN_ExternMethodNoImplementation, "set").WithArguments("AbstractGoo.IGoo.Property11.set")); } @@ -335,9 +342,16 @@ static event System.Action IGoo.Event12 { add { } remove { } } // (34,55): error CS0179: 'AbstractGoo.IGoo.Event11.remove' cannot be extern and declare a body // extern event System.Action IGoo.Event11 { add { } remove { } } Diagnostic(ErrorCode.ERR_ExternHasBody, "remove").WithArguments("AbstractGoo.IGoo.Event11.remove"), - // (35,37): error CS0106: The modifier 'static' is not valid for this item + // (35,37): error CS8703: The modifier 'static' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // static event System.Action IGoo.Event12 { add { } remove { } } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "Event12").WithArguments("static", "9.0", "preview").WithLocation(35, 37), + // (35,37): error CS0539: 'AbstractGoo.Event12' in explicit interface declaration is not found among members of the interface that can be implemented // static event System.Action IGoo.Event12 { add { } remove { } } - Diagnostic(ErrorCode.ERR_BadMemberFlag, "Event12").WithArguments("static")); + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "Event12").WithArguments("AbstractGoo.Event12").WithLocation(35, 37), + // (18,30): error CS0535: 'AbstractGoo' does not implement interface member 'IGoo.Event12' + // abstract class AbstractGoo : IGoo + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "IGoo").WithArguments("AbstractGoo", "IGoo.Event12").WithLocation(18, 30) + ); } [Fact] // can't bind to events diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs index 0ccbe1b13f158..088dc75fc34f9 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs @@ -51809,6 +51809,9 @@ class Test1 : I2 // (4,25): error CS0065: 'I1.P1': event property must have both add and remove accessors // event System.Action P1 {add => throw null;} Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "P1").WithArguments("I1.P1").WithLocation(4, 25), + // (9,37): error CS0550: 'I2.I1.P1.remove' adds an accessor not found in interface member 'I1.P1' + // abstract event System.Action I1.P1; + Diagnostic(ErrorCode.ERR_ExplicitPropertyAddingAccessor, "P1").WithArguments("I2.I1.P1.remove", "I1.P1").WithLocation(9, 37), // (12,15): error CS0535: 'Test1' does not implement interface member 'I1.P1' // class Test1 : I2 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I2").WithArguments("Test1", "I1.P1").WithLocation(12, 15) @@ -51845,6 +51848,9 @@ class Test1 : I2 // (10,5): error CS8712: 'I2.I1.P1': abstract event cannot use event accessor syntax // { Diagnostic(ErrorCode.ERR_AbstractEventHasAccessors, "{").WithArguments("I2.I1.P1").WithLocation(10, 5), + // (12,9): error CS0550: 'I2.I1.P1.remove' adds an accessor not found in interface member 'I1.P1' + // remove {} + Diagnostic(ErrorCode.ERR_ExplicitPropertyAddingAccessor, "remove").WithArguments("I2.I1.P1.remove", "I1.P1").WithLocation(12, 9), // (16,15): error CS0535: 'Test1' does not implement interface member 'I1.P1' // class Test1 : I2 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I2").WithArguments("Test1", "I1.P1").WithLocation(16, 15) @@ -51874,6 +51880,9 @@ class Test1 : I2 // (4,25): error CS0065: 'I1.P1': event property must have both add and remove accessors // event System.Action P1 {remove => throw null;} Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "P1").WithArguments("I1.P1").WithLocation(4, 25), + // (9,37): error CS0550: 'I2.I1.P1.add' adds an accessor not found in interface member 'I1.P1' + // abstract event System.Action I1.P1; + Diagnostic(ErrorCode.ERR_ExplicitPropertyAddingAccessor, "P1").WithArguments("I2.I1.P1.add", "I1.P1").WithLocation(9, 37), // (12,15): error CS0535: 'Test1' does not implement interface member 'I1.P1' // class Test1 : I2 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I2").WithArguments("Test1", "I1.P1").WithLocation(12, 15) @@ -51910,6 +51919,9 @@ class Test1 : I2 // (10,5): error CS8712: 'I2.I1.P1': abstract event cannot use event accessor syntax // { Diagnostic(ErrorCode.ERR_AbstractEventHasAccessors, "{").WithArguments("I2.I1.P1").WithLocation(10, 5), + // (11,9): error CS0550: 'I2.I1.P1.add' adds an accessor not found in interface member 'I1.P1' + // add {} + Diagnostic(ErrorCode.ERR_ExplicitPropertyAddingAccessor, "add").WithArguments("I2.I1.P1.add", "I1.P1").WithLocation(11, 9), // (16,15): error CS0535: 'Test1' does not implement interface member 'I1.P1' // class Test1 : I2 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I2").WithArguments("Test1", "I1.P1").WithLocation(16, 15) diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index c24b2e7ef1543..6849a7dd9d367 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -11515,18 +11515,17 @@ public class C3 : C2, I1 } "; - var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.ToMetadataReference() }); - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); - - compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.EmitToImageReference() }); - - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } void validate(ModuleSymbol module) { @@ -11622,6 +11621,8 @@ public class C5 : C2, I1 parseOptions: TestOptions.RegularPreview, targetFramework: TargetFramework.NetCoreApp); + compilation1.VerifyDiagnostics(); + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); var m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); @@ -11642,6 +11643,12 @@ public class C5 : C2, I1 var c5 = compilation1.GlobalNamespace.GetTypeMember("C5"); Assert.Equal("void C2.M01()", c5.FindImplementationForInterfaceMember(m01).ToTestDisplayString()); + + compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); } [Fact] @@ -11680,6 +11687,12 @@ public class C1 : I1 Assert.Null(c1.FindImplementationForInterfaceMember(m01)); Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyEmitDiagnostics(); + var source2 = @" public class C1 : I1 @@ -11754,6 +11767,9 @@ public class C1 : I2 Assert.Null(c1.FindImplementationForInterfaceMember(m01)); Assert.Null(i2.FindImplementationForInterfaceMember(m01)); + + var i2M01 = i2.GetMembers().OfType().Single(); + Assert.Same(m01, i2M01.ExplicitInterfaceImplementations.Single()); } [Fact] @@ -11813,6 +11829,7 @@ void validate(ModuleSymbol module) Assert.False(c1M01.IsMetadataFinal); Assert.False(c1M01.IsMetadataNewSlot()); Assert.Equal(MethodKind.Ordinary, c1M01.MethodKind); + Assert.Empty(c1M01.ExplicitInterfaceImplementations); } else { @@ -11946,6 +11963,7 @@ public void ImplementAbstractStaticMethod_15() public interface I1 { abstract static void M01(); + abstract static void M02(); } public class C1 @@ -11955,6 +11973,7 @@ public static void M01() {} public class C2 : C1, I1 { + static void I1.M02() {} } "; @@ -11971,23 +11990,24 @@ public class C3 : C2, I1 } "; - var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.ToMetadataReference() }); - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); - - compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.EmitToImageReference() }); - - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.RegularPreview, TestOptions.Regular9 }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } void validate(ModuleSymbol module) { var c3 = module.GlobalNamespace.GetTypeMember("C3"); - var m01 = c3.Interfaces().Single().GetMembers().OfType().Single(); + Assert.Empty(c3.GetMembers().OfType().Where(m => !m.IsConstructor())); + + var m01 = c3.Interfaces().Single().GetMembers("M01").OfType().Single(); var c1M01 = c3.BaseType().BaseType().GetMember("M01"); Assert.Equal("void C1.M01()", c1M01.ToTestDisplayString()); @@ -12011,6 +12031,12 @@ void validate(ModuleSymbol module) { Assert.Same(c1M01, c3.FindImplementationForInterfaceMember(m01)); } + + var m02 = c3.Interfaces().Single().GetMembers("M02").OfType().Single(); + + var c2M02 = c3.BaseType().GetMember("I1.M02"); + Assert.Equal("void C2.I1.M02()", c2M02.ToTestDisplayString()); + Assert.Same(c2M02, c3.FindImplementationForInterfaceMember(m02)); } } @@ -12050,30 +12076,17 @@ public class C3 : C2, I1 } "; - var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.ToMetadataReference() }); - var verifier = CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); - - verifier.VerifyIL("C3.I1.M01()", -@" -{ - // Code size 6 (0x6) - .maxstack 0 - IL_0000: call ""void C2.M01()"" - IL_0005: ret -} -"); - - compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.EmitToImageReference() }); - - verifier = CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + var verifier = CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); - verifier.VerifyIL("C3.I1.M01()", + verifier.VerifyIL("C3.I1.M01()", @" { // Code size 6 (0x6) @@ -12082,6 +12095,8 @@ .maxstack 0 IL_0005: ret } "); + } + } void validate(ModuleSymbol module) { @@ -12155,19 +12170,18 @@ public class C2 : C1, I1 } "; - var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.ToMetadataReference() }); - - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); - - compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.EmitToImageReference() }); + references: new[] { reference }); - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } void validate(ModuleSymbol module) { @@ -12239,19 +12253,18 @@ public class C2 : C1, I1 } "; - var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.ToMetadataReference() }); - - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); - - compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.EmitToImageReference() }); + references: new[] { reference }); - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } void validate(ModuleSymbol module) { @@ -12321,19 +12334,18 @@ public class C2 : C1, I1 } "; - var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.ToMetadataReference() }); - - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); - - compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.EmitToImageReference() }); + references: new[] { reference }); - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } void validate(ModuleSymbol module) { @@ -12392,18 +12404,18 @@ public class C2 : C1, I1 } "; - var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.ToMetadataReference() }); - - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + references: new[] { reference }); - compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.EmitToImageReference() }); - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } void validate(ModuleSymbol module) { @@ -12465,19 +12477,18 @@ public class C2 : C11, I1 } "; - var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.ToMetadataReference() }); - - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); - - compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.EmitToImageReference() }); + references: new[] { reference }); - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } void validate(ModuleSymbol module) { @@ -12552,19 +12563,18 @@ public class C2 : C11, I1 } "; - var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.ToMetadataReference() }); - - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); - - compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.EmitToImageReference() }); + references: new[] { reference }); - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } void validate(ModuleSymbol module) { @@ -14015,18 +14025,18 @@ public class C3 : C2, I1 "; var opName = UnaryOperatorName(op); - var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.ToMetadataReference() }); - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); - - compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.EmitToImageReference() }); - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } void validate(ModuleSymbol module) { @@ -14088,18 +14098,18 @@ public class C3 : C2, I1 "; var opName = UnaryOperatorName(op); - var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.ToMetadataReference() }); - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); - - compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.EmitToImageReference() }); - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } void validate(ModuleSymbol module) { @@ -14164,18 +14174,18 @@ public class C3 : C2, I1 "; var opName = BinaryOperatorName(op); - var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.ToMetadataReference() }); - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); - compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.EmitToImageReference() }); - - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } void validate(ModuleSymbol module) { @@ -14278,6 +14288,8 @@ public class C5 : C2, I1 parseOptions: TestOptions.RegularPreview, targetFramework: TargetFramework.NetCoreApp); + compilation1.VerifyDiagnostics(); + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); var m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); @@ -14304,6 +14316,12 @@ public class C5 : C2, I1 var c2M01 = (MethodSymbol)c5.FindImplementationForInterfaceMember(m01); Assert.Equal("I1 C2." + opName + "(I1 x)", c2M01.ToTestDisplayString()); Assert.Equal(MethodKind.UserDefinedOperator, c2M01.MethodKind); + + compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); } [Theory] @@ -14395,6 +14413,8 @@ public class C5 : C2, I1 parseOptions: TestOptions.RegularPreview, targetFramework: TargetFramework.NetCoreApp); + compilation1.VerifyDiagnostics(); + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); var m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); @@ -14421,6 +14441,12 @@ public class C5 : C2, I1 var c2M01 = (MethodSymbol)c5.FindImplementationForInterfaceMember(m01); Assert.Equal("I1 C2." + opName + "(I1 x, System.Int32 y)", c2M01.ToTestDisplayString()); Assert.Equal(MethodKind.UserDefinedOperator, c2M01.MethodKind); + + compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); } [Theory] @@ -14466,6 +14492,12 @@ public class C1 : I1 Assert.Null(c1.FindImplementationForInterfaceMember(m01)); Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyEmitDiagnostics(); + var source2 = @" public class C1 : I1 @@ -14535,6 +14567,12 @@ public class C1 : I1 Assert.Null(c1.FindImplementationForInterfaceMember(m01)); Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyEmitDiagnostics(); + var source2 = @" public class C1 : I1 @@ -14617,6 +14655,9 @@ public class C1 : I2 Assert.Equal(MethodKind.UserDefinedOperator, m01.MethodKind); Assert.Null(c1.FindImplementationForInterfaceMember(m01)); Assert.Null(i2.FindImplementationForInterfaceMember(m01)); + + var i2M01 = i2.GetMembers().OfType().Single(); + Assert.Same(m01, i2M01.ExplicitInterfaceImplementations.Single()); } [Theory] @@ -14677,6 +14718,9 @@ public class C1 : I2 Assert.Equal(MethodKind.UserDefinedOperator, m01.MethodKind); Assert.Null(c1.FindImplementationForInterfaceMember(m01)); Assert.Null(i2.FindImplementationForInterfaceMember(m01)); + + var i2M01 = i2.GetMembers().OfType().Single(); + Assert.Same(m01, i2M01.ExplicitInterfaceImplementations.Single()); } [Theory] @@ -14761,6 +14805,7 @@ void validate(ModuleSymbol module) Assert.Equal(MethodKind.UserDefinedOperator, c1M01.MethodKind); Assert.False(c1M01.HasRuntimeSpecialName); Assert.True(c1M01.HasSpecialName); + Assert.Empty(c1M01.ExplicitInterfaceImplementations); } else { @@ -15136,44 +15181,82 @@ .maxstack 2 [Theory] [CombinatorialData] - public void ImplementAbstractStaticBinaryOperator_15([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=")] string op) + public void ImplementAbstractStaticUnaryOperator_15([CombinatorialValues("+", "-", "!", "~", "++", "--")] string op) { // A forwarding method isn't created if base class implements interface exactly the same way. var source1 = @" -public partial interface I1 where T : I1 +public interface I1 { - abstract static T operator " + op + @"(T x, C1 y); + abstract static I1 operator " + op + @"(I1 x); } -public partial class C1 +public partial class C2 : I1 { - public static C2 operator " + op + @"(C2 x, C1 y) => default; + static I1 I1.operator " + op + @"(I1 x) => default; } +"; -public class C2 : C1, I1 + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C3 : C2, I1 { } "; - string matchingOp = MatchingBinaryOperator(op); + var opName = UnaryOperatorName(op); - if (matchingOp is object) + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) { - source1 += + foreach (var parseOptions in new[] { TestOptions.RegularPreview, TestOptions.Regular9 }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } + + void validate(ModuleSymbol module) + { + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + Assert.Empty(c3.GetMembers().OfType().Where(m => !m.IsConstructor())); + + var m02 = c3.Interfaces().Single().GetMembers(opName).OfType().Single(); + + var c2M02 = c3.BaseType().GetMembers("I1." + opName).OfType().Single(); + Assert.Equal("I1 C2.I1." + opName + "(I1 x)", c2M02.ToTestDisplayString()); + Assert.Same(c2M02, c3.FindImplementationForInterfaceMember(m02)); + } + } + + [Fact] + public void ImplementAbstractStaticUnaryTrueFalseOperator_15() + { + // A forwarding method isn't created if base class implements interface exactly the same way. + + var source1 = @" -public partial interface I1 +public interface I1 { - abstract static T operator " + matchingOp + @"(T x, C1 y); + abstract static bool operator true(I1 x); + abstract static bool operator false(I1 x); } -public partial class C1 +public partial class C2 : I1 { - public static C2 operator " + matchingOp + @"(C2 x, C1 y) => default; + static bool I1.operator true(I1 x) => default; + static bool I1.operator false(I1 x) => default; } "; - } var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, parseOptions: TestOptions.RegularPreview, @@ -15183,32 +15266,126 @@ public partial class C1 var source2 = @" -public class C3 : C2, I1 +public class C3 : C2, I1 { } "; - var opName = BinaryOperatorName(op); - - var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.ToMetadataReference() }); - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); - - compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.EmitToImageReference() }); - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.RegularPreview, TestOptions.Regular9 }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } void validate(ModuleSymbol module) { var c3 = module.GlobalNamespace.GetTypeMember("C3"); - var m01 = c3.Interfaces().Single().GetMembers(opName).OfType().Single(); + Assert.Empty(c3.GetMembers().OfType().Where(m => !m.IsConstructor())); - var c1M01 = c3.BaseType().BaseType().GetMember(opName); + var m01 = c3.Interfaces().Single().GetMembers("op_True").OfType().Single(); + var m02 = c3.Interfaces().Single().GetMembers("op_False").OfType().Single(); + + var c2M01 = c3.BaseType().GetMembers("I1.op_True").OfType().Single(); + Assert.Equal("System.Boolean C2.I1.op_True(I1 x)", c2M01.ToTestDisplayString()); + Assert.Same(c2M01, c3.FindImplementationForInterfaceMember(m01)); + + var c2M02 = c3.BaseType().GetMembers("I1.op_False").OfType().Single(); + Assert.Equal("System.Boolean C2.I1.op_False(I1 x)", c2M02.ToTestDisplayString()); + Assert.Same(c2M02, c3.FindImplementationForInterfaceMember(m02)); + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticBinaryOperator_15([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=")] string op) + { + // A forwarding method isn't created if base class implements interface exactly the same way. + + var source1 = +@" +public partial interface I1 where T : I1 +{ + abstract static T operator " + op + @"(T x, C1 y); + abstract static T operator " + op + @"(T x, C2 y); +} + +public partial class C1 +{ + public static C2 operator " + op + @"(C2 x, C1 y) => default; +} + +public partial class C2 : C1, I1 +{ + static C2 I1.operator " + op + @"(C2 x, C2 y) => default; +} +"; + + string matchingOp = MatchingBinaryOperator(op); + + if (matchingOp is object) + { + source1 += +@" +public partial interface I1 +{ + abstract static T operator " + matchingOp + @"(T x, C1 y); + abstract static T operator " + matchingOp + @"(T x, C2 y); +} + +public partial class C1 +{ + public static C2 operator " + matchingOp + @"(C2 x, C1 y) => default; +} + +public partial class C2 +{ + static C2 I1.operator " + matchingOp + @"(C2 x, C2 y) => default; +} +"; + } + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C3 : C2, I1 +{ +} +"; + + var opName = BinaryOperatorName(op); + + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.RegularPreview, TestOptions.Regular9 }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } + + void validate(ModuleSymbol module) + { + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + Assert.Empty(c3.GetMembers().OfType().Where(m => !m.IsConstructor())); + + var m01 = c3.Interfaces().Single().GetMembers(opName).OfType().First(); + + var c1M01 = c3.BaseType().BaseType().GetMember(opName); Assert.Equal("C2 C1." + opName + "(C2 x, C1 y)", c1M01.ToTestDisplayString()); Assert.True(c1M01.IsStatic); @@ -15230,6 +15407,12 @@ void validate(ModuleSymbol module) { Assert.Same(c1M01, c3.FindImplementationForInterfaceMember(m01)); } + + var m02 = c3.Interfaces().Single().GetMembers(opName).OfType().ElementAt(1); + + var c2M02 = c3.BaseType().GetMembers("I1." + opName).OfType().First(); + Assert.Equal("C2 C2.I1." + opName + "(C2 x, C2 y)", c2M02.ToTestDisplayString()); + Assert.Same(c2M02, c3.FindImplementationForInterfaceMember(m02)); } } @@ -15294,32 +15477,17 @@ public class C3 : C2, I1 var opName = BinaryOperatorName(op); - var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.ToMetadataReference() }); - var verifier = CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); - - verifier.VerifyIL("C3.I1." + opName + "(C2, C1)", -@" -{ - // Code size 8 (0x8) - .maxstack 2 - IL_0000: ldarg.0 - IL_0001: ldarg.1 - IL_0002: call ""C2 C2." + opName + @"(C2, C1)"" - IL_0007: ret -} -"); - - compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.EmitToImageReference() }); - - verifier = CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + var verifier = CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); - verifier.VerifyIL("C3.I1." + opName + "(C2, C1)", + verifier.VerifyIL("C3.I1." + opName + "(C2, C1)", @" { // Code size 8 (0x8) @@ -15330,6 +15498,8 @@ .maxstack 2 IL_0007: ret } "); + } + } void validate(ModuleSymbol module) { @@ -15424,19 +15594,18 @@ public class C2 : C1, I1, int> } "; - var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.ToMetadataReference() }); - - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); - - compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.EmitToImageReference() }); + references: new[] { reference }); - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } void validate(ModuleSymbol module) { @@ -15526,19 +15695,18 @@ public class C2 : C1, I1, int> } "; - var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.ToMetadataReference() }); - - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); - - compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.EmitToImageReference() }); + references: new[] { reference }); - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } void validate(ModuleSymbol module) { @@ -15621,19 +15789,18 @@ public class C2 : C11, I1, C1> } "; - var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.ToMetadataReference() }); + references: new[] { reference }); - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); - - compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, - parseOptions: TestOptions.RegularPreview, - targetFramework: TargetFramework.NetCoreApp, - references: new[] { compilation1.EmitToImageReference() }); - - CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } void validate(ModuleSymbol module) { @@ -16051,5 +16218,4893 @@ struct C2 : I1 Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "x").WithArguments("I1", "T", "C2").WithLocation(14, 44 + op.Length - 1) ); } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticProperty_01(bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static int M01 { get; set; } +} + +" + typeKeyword + @" + C1 : I1 +{} + +" + typeKeyword + @" + C2 : I1 +{ + public int M01 { get; set; } +} + +" + typeKeyword + @" + C3 : I1 +{ + static int M01 { get; set; } +} + +" + typeKeyword + @" + C4 : I1 +{ + int I1.M01 { get; set; } +} + +" + typeKeyword + @" + C5 : I1 +{ + public static long M01 { get; set; } +} + +" + typeKeyword + @" + C6 : I1 +{ + static long I1.M01 { get; set; } +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (8,10): error CS0535: 'C1' does not implement interface member 'I1.M01' + // C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01").WithLocation(8, 10), + // (12,10): error CS9109: 'C2' does not implement static interface member 'I1.M01'. 'C2.M01' cannot implement the interface member because it is not static. + // C2 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotStatic, "I1").WithArguments("C2", "I1.M01", "C2.M01").WithLocation(12, 10), + // (18,10): error CS0737: 'C3' does not implement interface member 'I1.M01'. 'C3.M01' cannot implement an interface member because it is not public. + // C3 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotPublic, "I1").WithArguments("C3", "I1.M01", "C3.M01").WithLocation(18, 10), + // (24,10): error CS0535: 'C4' does not implement interface member 'I1.M01' + // C4 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C4", "I1.M01").WithLocation(24, 10), + // (26,12): error CS0539: 'C4.M01' in explicit interface declaration is not found among members of the interface that can be implemented + // int I1.M01 { get; set; } + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("C4.M01").WithLocation(26, 12), + // (30,10): error CS0738: 'C5' does not implement interface member 'I1.M01'. 'C5.M01' cannot implement 'I1.M01' because it does not have the matching return type of 'int'. + // C5 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberWrongReturnType, "I1").WithArguments("C5", "I1.M01", "C5.M01", "int").WithLocation(30, 10), + // (36,10): error CS0535: 'C6' does not implement interface member 'I1.M01' + // C6 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C6", "I1.M01").WithLocation(36, 10), + // (38,20): error CS0539: 'C6.M01' in explicit interface declaration is not found among members of the interface that can be implemented + // static long I1.M01 { get; set; } + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("C6.M01").WithLocation(38, 20) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticProperty_02(bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract int M01 { get; set; } +} + +" + typeKeyword + @" + C1 : I1 +{} + +" + typeKeyword + @" + C2 : I1 +{ + public static int M01 { get; set; } +} + +" + typeKeyword + @" + C3 : I1 +{ + int M01 { get; set; } +} + +" + typeKeyword + @" + C4 : I1 +{ + static int I1.M01 { get; set; } +} + +" + typeKeyword + @" + C5 : I1 +{ + public long M01 { get; set; } +} + +" + typeKeyword + @" + C6 : I1 +{ + long I1.M01 { get; set; } +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (8,10): error CS0535: 'C1' does not implement interface member 'I1.M01' + // C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01").WithLocation(8, 10), + // (12,10): error CS0736: 'C2' does not implement instance interface member 'I1.M01'. 'C2.M01' cannot implement the interface member because it is static. + // C2 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberStatic, "I1").WithArguments("C2", "I1.M01", "C2.M01").WithLocation(12, 10), + // (18,10): error CS0737: 'C3' does not implement interface member 'I1.M01'. 'C3.M01' cannot implement an interface member because it is not public. + // C3 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotPublic, "I1").WithArguments("C3", "I1.M01", "C3.M01").WithLocation(18, 10), + // (24,10): error CS0535: 'C4' does not implement interface member 'I1.M01' + // C4 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C4", "I1.M01").WithLocation(24, 10), + // (26,19): error CS0539: 'C4.M01' in explicit interface declaration is not found among members of the interface that can be implemented + // static int I1.M01 { get; set; } + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("C4.M01").WithLocation(26, 19), + // (30,10): error CS0738: 'C5' does not implement interface member 'I1.M01'. 'C5.M01' cannot implement 'I1.M01' because it does not have the matching return type of 'int'. + // C5 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberWrongReturnType, "I1").WithArguments("C5", "I1.M01", "C5.M01", "int").WithLocation(30, 10), + // (36,10): error CS0535: 'C6' does not implement interface member 'I1.M01' + // C6 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C6", "I1.M01").WithLocation(36, 10), + // (38,13): error CS0539: 'C6.M01' in explicit interface declaration is not found among members of the interface that can be implemented + // long I1.M01 { get; set; } + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("C6.M01").WithLocation(38, 13) + ); + } + + [Fact] + public void ImplementAbstractStaticProperty_03() + { + var source1 = +@" +public interface I1 +{ + abstract static int M01 { get; set; } +} + +interface I2 : I1 +{} + +interface I3 : I1 +{ + public virtual int M01 { get => 0; set{} } +} + +interface I4 : I1 +{ + static int M01 { get; set; } +} + +interface I5 : I1 +{ + int I1.M01 { get => 0; set{} } +} + +interface I6 : I1 +{ + static int I1.M01 { get => 0; set{} } +} + +interface I7 : I1 +{ + abstract static int M01 { get; set; } +} + +interface I8 : I1 +{ + abstract static int I1.M01 { get; set; } +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (12,24): warning CS0108: 'I3.M01' hides inherited member 'I1.M01'. Use the new keyword if hiding was intended. + // public virtual int M01 { get => 0; set{} } + Diagnostic(ErrorCode.WRN_NewRequired, "M01").WithArguments("I3.M01", "I1.M01").WithLocation(12, 24), + // (17,16): warning CS0108: 'I4.M01' hides inherited member 'I1.M01'. Use the new keyword if hiding was intended. + // static int M01 { get; set; } + Diagnostic(ErrorCode.WRN_NewRequired, "M01").WithArguments("I4.M01", "I1.M01").WithLocation(17, 16), + // (22,12): error CS0539: 'I5.M01' in explicit interface declaration is not found among members of the interface that can be implemented + // int I1.M01 { get => 0; set{} } + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("I5.M01").WithLocation(22, 12), + // (27,19): error CS0106: The modifier 'static' is not valid for this item + // static int I1.M01 { get => 0; set{} } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M01").WithArguments("static").WithLocation(27, 19), + // (27,19): error CS0539: 'I6.M01' in explicit interface declaration is not found among members of the interface that can be implemented + // static int I1.M01 { get => 0; set{} } + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("I6.M01").WithLocation(27, 19), + // (32,25): warning CS0108: 'I7.M01' hides inherited member 'I1.M01'. Use the new keyword if hiding was intended. + // abstract static int M01 { get; set; } + Diagnostic(ErrorCode.WRN_NewRequired, "M01").WithArguments("I7.M01", "I1.M01").WithLocation(32, 25), + // (37,28): error CS0106: The modifier 'static' is not valid for this item + // abstract static int I1.M01 { get; set; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M01").WithArguments("static").WithLocation(37, 28), + // (37,28): error CS0539: 'I8.M01' in explicit interface declaration is not found among members of the interface that can be implemented + // abstract static int I1.M01 { get; set; } + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("I8.M01").WithLocation(37, 28) + ); + + foreach (var m01 in compilation1.GlobalNamespace.GetTypeMember("I1").GetMembers()) + { + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I2").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I3").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I4").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I5").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I6").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I7").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I8").FindImplementationForInterfaceMember(m01)); + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticProperty_04(bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static int M01 { get; set; } + abstract static int M02 { get; set; } +} +"; + var source2 = +typeKeyword + @" + Test: I1 +{ + static int I1.M01 { get; set; } + public static int M02 { get; set; } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (4,19): error CS8703: The modifier 'static' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // static int I1.M01 { get; set; } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("static", "9.0", "preview").WithLocation(4, 19) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation3.VerifyDiagnostics( + // (4,19): error CS8703: The modifier 'static' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // static int I1.M01 { get; set; } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("static", "9.0", "preview").WithLocation(4, 19), + // (10,25): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static int M01 { get; set; } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "9.0", "preview").WithLocation(10, 25), + // (11,25): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static int M02 { get; set; } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M02").WithArguments("abstract", "9.0", "preview").WithLocation(11, 25) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticProperty_05(bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static int M01 { get; set; } +} +"; + var source2 = +typeKeyword + @" + Test1: I1 +{ + public static int M01 { get; set; } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (2,12): error CS9110: 'Test1.M01.set' cannot implement interface member 'I1.M01.set' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01.set", "I1.M01.set", "Test1").WithLocation(2, 12), + // (2,12): error CS9110: 'Test1.M01.get' cannot implement interface member 'I1.M01.get' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01.get", "I1.M01.get", "Test1").WithLocation(2, 12), + // (2,12): error CS9110: 'Test1.M01' cannot implement interface member 'I1.M01' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01", "I1.M01", "Test1").WithLocation(2, 12) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation3.VerifyDiagnostics( + // (2,12): error CS9110: 'Test1.M01.set' cannot implement interface member 'I1.M01.set' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01.set", "I1.M01.set", "Test1").WithLocation(2, 12), + // (2,12): error CS9110: 'Test1.M01.get' cannot implement interface member 'I1.M01.get' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01.get", "I1.M01.get", "Test1").WithLocation(2, 12), + // (2,12): error CS9110: 'Test1.M01' cannot implement interface member 'I1.M01' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01", "I1.M01", "Test1").WithLocation(2, 12), + // (9,31): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static int M01 { get; set; } + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "get").WithLocation(9, 31), + // (9,36): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static int M01 { get; set; } + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "set").WithLocation(9, 36) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticProperty_06(bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static int M01 { get; set; } +} +"; + var source2 = +typeKeyword + @" + Test1: I1 +{ + static int I1.M01 { get; set; } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (4,19): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // static int I1.M01 { get; set; } + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(4, 19) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation3.VerifyDiagnostics( + // (4,19): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // static int I1.M01 { get; set; } + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(4, 19), + // (9,31): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static int M01 { get; set; } + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "get").WithLocation(9, 31), + // (9,36): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static int M01 { get; set; } + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "set").WithLocation(9, 36) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticProperty_07(bool structure) + { + // Basic implicit implementation scenario, MethodImpl is emitted + + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static int M01 { get; set; } +} + +" + typeKeyword + @" + C : I1 +{ + public static int M01 { get => 0; set {} } +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped, + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false)).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var m01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); + var m01Get = m01.GetMethod; + var m01Set = m01.SetMethod; + var c = module.GlobalNamespace.GetTypeMember("C"); + + Assert.Equal(1, c.GetMembers().OfType().Count()); + Assert.Equal(2, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var cM01 = (PropertySymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + + Assert.Equal("System.Int32 C.M01 { get; set; }", cM01.ToTestDisplayString()); + + var cM01Get = cM01.GetMethod; + Assert.Same(cM01Get, c.FindImplementationForInterfaceMember(m01Get)); + + Assert.True(cM01Get.IsStatic); + Assert.False(cM01Get.IsAbstract); + Assert.False(cM01Get.IsVirtual); + Assert.False(cM01Get.IsMetadataVirtual()); + Assert.False(cM01Get.IsMetadataFinal); + Assert.False(cM01Get.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertyGet, cM01Get.MethodKind); + Assert.False(cM01Get.HasRuntimeSpecialName); + Assert.True(cM01Get.HasSpecialName); + + Assert.Equal("System.Int32 C.M01.get", cM01Get.ToTestDisplayString()); + + var cM01Set = cM01.SetMethod; + Assert.Same(cM01Set, c.FindImplementationForInterfaceMember(m01Set)); + + Assert.True(cM01Set.IsStatic); + Assert.False(cM01Set.IsAbstract); + Assert.False(cM01Set.IsVirtual); + Assert.False(cM01Set.IsMetadataVirtual()); + Assert.False(cM01Set.IsMetadataFinal); + Assert.False(cM01Set.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertySet, cM01Set.MethodKind); + Assert.False(cM01Set.HasRuntimeSpecialName); + Assert.True(cM01Set.HasSpecialName); + + Assert.Equal("void C.M01.set", cM01Set.ToTestDisplayString()); + + if (module is PEModuleSymbol) + { + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01Get, cM01Get.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01Set, cM01Set.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(cM01.ExplicitInterfaceImplementations); + Assert.Empty(cM01Get.ExplicitInterfaceImplementations); + Assert.Empty(cM01Set.ExplicitInterfaceImplementations); + } + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticReadonlyProperty_07(bool structure) + { + // Basic implicit implementation scenario, MethodImpl is emitted + + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static int M01 { get; } +} + +" + typeKeyword + @" + C : I1 +{ + public static int M01 { get; set; } +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped, + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false)).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var m01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); + var m01Get = m01.GetMethod; + Assert.Null(m01.SetMethod); + + var c = module.GlobalNamespace.GetTypeMember("C"); + + Assert.Equal(1, c.GetMembers().OfType().Count()); + Assert.Equal(2, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var cM01 = (PropertySymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + + Assert.Equal("System.Int32 C.M01 { get; set; }", cM01.ToTestDisplayString()); + + var cM01Get = cM01.GetMethod; + Assert.Same(cM01Get, c.FindImplementationForInterfaceMember(m01Get)); + + Assert.True(cM01Get.IsStatic); + Assert.False(cM01Get.IsAbstract); + Assert.False(cM01Get.IsVirtual); + Assert.False(cM01Get.IsMetadataVirtual()); + Assert.False(cM01Get.IsMetadataFinal); + Assert.False(cM01Get.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertyGet, cM01Get.MethodKind); + + Assert.Equal("System.Int32 C.M01.get", cM01Get.ToTestDisplayString()); + + var cM01Set = cM01.SetMethod; + + Assert.True(cM01Set.IsStatic); + Assert.False(cM01Set.IsAbstract); + Assert.False(cM01Set.IsVirtual); + Assert.False(cM01Set.IsMetadataVirtual()); + Assert.False(cM01Set.IsMetadataFinal); + Assert.False(cM01Set.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertySet, cM01Set.MethodKind); + + Assert.Equal("void C.M01.set", cM01Set.ToTestDisplayString()); + + if (module is PEModuleSymbol) + { + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01Get, cM01Get.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(cM01.ExplicitInterfaceImplementations); + Assert.Empty(cM01Get.ExplicitInterfaceImplementations); + } + + Assert.Empty(cM01Set.ExplicitInterfaceImplementations); + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticProperty_08(bool structure) + { + // Basic explicit implementation scenario + + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static int M01 { get; set; } +} + +" + typeKeyword + @" + C : I1 +{ + static int I1.M01 { get => 0; set {} } +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped, + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false)).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var m01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); + var m01Get = m01.GetMethod; + var m01Set = m01.SetMethod; + var c = module.GlobalNamespace.GetTypeMember("C"); + + Assert.Equal(1, c.GetMembers().OfType().Count()); + Assert.Equal(2, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var cM01 = (PropertySymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + + Assert.Equal("System.Int32 C.I1.M01 { get; set; }", cM01.ToTestDisplayString()); + + var cM01Get = cM01.GetMethod; + Assert.Same(cM01Get, c.FindImplementationForInterfaceMember(m01Get)); + + Assert.True(cM01Get.IsStatic); + Assert.False(cM01Get.IsAbstract); + Assert.False(cM01Get.IsVirtual); + Assert.False(cM01Get.IsMetadataVirtual()); + Assert.False(cM01Get.IsMetadataFinal); + Assert.False(cM01Get.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertyGet, cM01Get.MethodKind); + Assert.False(cM01Get.HasRuntimeSpecialName); + Assert.True(cM01Get.HasSpecialName); + + Assert.Equal("System.Int32 C.I1.M01.get", cM01Get.ToTestDisplayString()); + + var cM01Set = cM01.SetMethod; + Assert.Same(cM01Set, c.FindImplementationForInterfaceMember(m01Set)); + + Assert.True(cM01Set.IsStatic); + Assert.False(cM01Set.IsAbstract); + Assert.False(cM01Set.IsVirtual); + Assert.False(cM01Set.IsMetadataVirtual()); + Assert.False(cM01Set.IsMetadataFinal); + Assert.False(cM01Set.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertySet, cM01Set.MethodKind); + Assert.False(cM01Set.HasRuntimeSpecialName); + Assert.True(cM01Set.HasSpecialName); + + Assert.Equal("void C.I1.M01.set", cM01Set.ToTestDisplayString()); + + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01Get, cM01Get.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01Set, cM01Set.ExplicitInterfaceImplementations.Single()); + } + } + + [Fact] + public void ImplementAbstractStaticProperty_09() + { + // Explicit implementation from base is treated as an implementation + + var source1 = +@" +public interface I1 +{ + abstract static int M01 { get; set; } +} + +public class C1 +{ + public static void M01() {} +} + +public class C2 : C1, I1 +{ + static int I1.M01 { get; set; } +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C3 : C2, I1 +{ +} +"; + + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } + + void validate(ModuleSymbol module) + { + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + Assert.Empty(c3.GetMembers().OfType()); + Assert.Empty(c3.GetMembers().OfType().Where(m => !m.IsConstructor())); + var m01 = c3.Interfaces().Single().GetMembers().OfType().Single(); + + var cM01 = (PropertySymbol)c3.FindImplementationForInterfaceMember(m01); + + Assert.Equal("System.Int32 C2.I1.M01 { get; set; }", cM01.ToTestDisplayString()); + + Assert.Same(cM01.GetMethod, c3.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Same(cM01.SetMethod, c3.FindImplementationForInterfaceMember(m01.SetMethod)); + + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.GetMethod, cM01.GetMethod.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.SetMethod, cM01.SetMethod.ExplicitInterfaceImplementations.Single()); + } + } + + [Fact] + public void ImplementAbstractStaticProperty_10() + { + // Implicit implementation is considered only for types implementing interface in source. + // In metadata, only explicit implementations are considered + + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig specialname abstract virtual static + int32 get_M01 () cil managed + { + } + + .method public hidebysig specialname abstract virtual static + void set_M01 ( + int32 'value' + ) cil managed + { + } + + .property int32 M01() + { + .get int32 I1::get_M01() + .set void I1::set_M01(int32) + } +} + +.class public auto ansi beforefieldinit C1 + extends System.Object + implements I1 +{ + .method private hidebysig specialname static + int32 I1.get_M01 () cil managed + { + .override method int32 I1::get_M01() + IL_0000: ldc.i4.0 + IL_0001: ret + } + + .method private hidebysig specialname static + void I1.set_M01 ( + int32 'value' + ) cil managed + { + .override method void I1::set_M01(int32) + IL_0000: ret + } + + .property instance int32 I1.M01() + { + .get int32 C1::I1.get_M01() + .set void C1::I1.set_M01(int32) + } + + .method public hidebysig specialname static + int32 get_M01 () cil managed + { + IL_0000: ldc.i4.0 + IL_0001: ret + } + + .method public hidebysig specialname static + void set_M01 ( + int32 'value' + ) cil managed + { + IL_0000: ret + } + + .property int32 M01() + { + .get int32 C1::get_M01() + .set void C1::set_M01(int32) + } + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + IL_0000: ldarg.0 + IL_0001: call instance void System.Object::.ctor() + IL_0006: ret + } +} + +.class public auto ansi beforefieldinit C2 + extends C1 + implements I1 +{ + .method public hidebysig specialname static + int32 get_M01 () cil managed + { + IL_0000: ldc.i4.0 + IL_0001: ret + } + + .method public hidebysig specialname static + void set_M01 ( + int32 'value' + ) cil managed + { + IL_0000: ret + } + + .property int32 M01() + { + .get int32 C2::get_M01() + .set void C2::set_M01(int32) + } + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + IL_0000: ldarg.0 + IL_0001: call instance void C1::.ctor() + IL_0006: ret + } +} +"; + var source1 = +@" +public class C3 : C2 +{ +} + +public class C4 : C1, I1 +{ +} + +public class C5 : C2, I1 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); + + var c1M01 = (PropertySymbol)c1.FindImplementationForInterfaceMember(m01); + + Assert.Equal("System.Int32 C1.I1.M01 { get; set; }", c1M01.ToTestDisplayString()); + + Assert.Same(c1M01.GetMethod, c1.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Same(c1M01.SetMethod, c1.FindImplementationForInterfaceMember(m01.SetMethod)); + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.GetMethod, c1M01.GetMethod.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.SetMethod, c1M01.SetMethod.ExplicitInterfaceImplementations.Single()); + + var c2 = compilation1.GlobalNamespace.GetTypeMember("C2"); + Assert.Same(c1M01, c2.FindImplementationForInterfaceMember(m01)); + Assert.Same(c1M01.GetMethod, c2.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Same(c1M01.SetMethod, c2.FindImplementationForInterfaceMember(m01.SetMethod)); + + var c3 = compilation1.GlobalNamespace.GetTypeMember("C3"); + Assert.Same(c1M01, c3.FindImplementationForInterfaceMember(m01)); + Assert.Same(c1M01.GetMethod, c3.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Same(c1M01.SetMethod, c3.FindImplementationForInterfaceMember(m01.SetMethod)); + + var c4 = compilation1.GlobalNamespace.GetTypeMember("C4"); + Assert.Same(c1M01, c4.FindImplementationForInterfaceMember(m01)); + Assert.Same(c1M01.GetMethod, c4.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Same(c1M01.SetMethod, c4.FindImplementationForInterfaceMember(m01.SetMethod)); + + var c5 = compilation1.GlobalNamespace.GetTypeMember("C5"); + + var c2M01 = (PropertySymbol)c5.FindImplementationForInterfaceMember(m01); + Assert.Equal("System.Int32 C2.M01 { get; set; }", c2M01.ToTestDisplayString()); + Assert.Same(c2M01.GetMethod, c5.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Same(c2M01.SetMethod, c5.FindImplementationForInterfaceMember(m01.SetMethod)); + + compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + } + + [Fact] + public void ImplementAbstractStaticProperty_11() + { + // Ignore invalid metadata (non-abstract static virtual method). + scenario1(); + scenario2(); + scenario3(); + + void scenario1() + { + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method private hidebysig specialname static virtual + int32 get_M01 () cil managed + { + IL_0000: ldc.i4.0 + IL_0001: ret + } + + .method private hidebysig specialname static virtual + void set_M01 ( + int32 'value' + ) cil managed + { + IL_0000: ret + } + + .property int32 M01() + { + .get int32 I1::get_M01() + .set void I1::set_M01(int32) + } +} +"; + + var source1 = +@" +public class C1 : I1 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyEmitDiagnostics(); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var i1 = c1.Interfaces().Single(); + var m01 = i1.GetMembers().OfType().Single(); + + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.SetMethod)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01.SetMethod)); + + compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyEmitDiagnostics(); + + var source2 = +@" +public class C1 : I1 +{ + static int I1.M01 { get; set; } +} +"; + + var compilation2 = CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation2.VerifyEmitDiagnostics( + // (4,18): error CS0539: 'C1.M01' in explicit interface declaration is not found among members of the interface that can be implemented + // static int I1.M01 { get; set; } + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("C1.M01").WithLocation(4, 18) + ); + + c1 = compilation2.GlobalNamespace.GetTypeMember("C1"); + m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); + + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.SetMethod)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01.SetMethod)); + + var source3 = +@" +public class C1 : I1 +{ + public static int M01 { get; set; } +} +"; + + var compilation3 = CreateCompilationWithIL(source3, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation3, sourceSymbolValidator: validate3, symbolValidator: validate3, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate3(ModuleSymbol module) + { + var c = module.GlobalNamespace.GetTypeMember("C1"); + Assert.Equal(1, c.GetMembers().OfType().Count()); + Assert.Equal(2, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var m01 = c.Interfaces().Single().GetMembers().OfType().Single(); + Assert.Null(c.FindImplementationForInterfaceMember(m01)); + Assert.Null(c.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Null(c.FindImplementationForInterfaceMember(m01.SetMethod)); + } + } + + void scenario2() + { + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig specialname abstract virtual static + int32 get_M01 () cil managed + { + } + + .method private hidebysig specialname static virtual + void set_M01 ( + int32 'value' + ) cil managed + { + IL_0000: ret + } + + .property int32 M01() + { + .get int32 I1::get_M01() + .set void I1::set_M01(int32) + } +} +"; + + var source1 = +@" +public class C1 : I1 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (2,19): error CS0535: 'C1' does not implement interface member 'I1.M01' + // public class C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01").WithLocation(2, 19) + ); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var i1 = c1.Interfaces().Single(); + var m01 = i1.GetMembers().OfType().Single(); + + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.SetMethod)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01.SetMethod)); + + var source2 = +@" +public class C1 : I1 +{ + static int I1.M01 { get; } +} +"; + + var compilation2 = CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate2, symbolValidator: validate2, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate2(ModuleSymbol module) + { + var c = module.GlobalNamespace.GetTypeMember("C1"); + var m01 = c.Interfaces().Single().GetMembers().OfType().Single(); + var m01Get = m01.GetMethod; + var m01Set = m01.SetMethod; + + Assert.Equal(1, c.GetMembers().OfType().Count()); + Assert.Equal(1, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var cM01 = (PropertySymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + + Assert.Equal("System.Int32 C1.I1.M01 { get; }", cM01.ToTestDisplayString()); + + var cM01Get = cM01.GetMethod; + Assert.Same(cM01Get, c.FindImplementationForInterfaceMember(m01Get)); + + Assert.True(cM01Get.IsStatic); + Assert.False(cM01Get.IsAbstract); + Assert.False(cM01Get.IsVirtual); + Assert.False(cM01Get.IsMetadataVirtual()); + Assert.False(cM01Get.IsMetadataFinal); + Assert.False(cM01Get.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertyGet, cM01Get.MethodKind); + + Assert.Equal("System.Int32 C1.I1.M01.get", cM01Get.ToTestDisplayString()); + + Assert.Null(cM01.SetMethod); + Assert.Null(c.FindImplementationForInterfaceMember(m01Set)); + + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01Get, cM01Get.ExplicitInterfaceImplementations.Single()); + } + + var source3 = +@" +public class C1 : I1 +{ + public static int M01 { get; set; } +} +"; + + var compilation3 = CreateCompilationWithIL(source3, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation3, sourceSymbolValidator: validate3, symbolValidator: validate3, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate3(ModuleSymbol module) + { + var c = module.GlobalNamespace.GetTypeMember("C1"); + + var m01 = c.Interfaces().Single().GetMembers().OfType().Single(); + var m01Get = m01.GetMethod; + + Assert.Equal(1, c.GetMembers().OfType().Count()); + Assert.Equal(2, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var cM01 = (PropertySymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + + Assert.Equal("System.Int32 C1.M01 { get; set; }", cM01.ToTestDisplayString()); + + var cM01Get = cM01.GetMethod; + Assert.Same(cM01Get, c.FindImplementationForInterfaceMember(m01Get)); + + Assert.True(cM01Get.IsStatic); + Assert.False(cM01Get.IsAbstract); + Assert.False(cM01Get.IsVirtual); + Assert.False(cM01Get.IsMetadataVirtual()); + Assert.False(cM01Get.IsMetadataFinal); + Assert.False(cM01Get.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertyGet, cM01Get.MethodKind); + + Assert.Equal("System.Int32 C1.M01.get", cM01Get.ToTestDisplayString()); + + var cM01Set = cM01.SetMethod; + + Assert.True(cM01Set.IsStatic); + Assert.False(cM01Set.IsAbstract); + Assert.False(cM01Set.IsVirtual); + Assert.False(cM01Set.IsMetadataVirtual()); + Assert.False(cM01Set.IsMetadataFinal); + Assert.False(cM01Set.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertySet, cM01Set.MethodKind); + + Assert.Equal("void C1.M01.set", cM01Set.ToTestDisplayString()); + + Assert.Null(c.FindImplementationForInterfaceMember(m01.SetMethod)); + + if (module is PEModuleSymbol) + { + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01Get, cM01Get.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(cM01.ExplicitInterfaceImplementations); + Assert.Empty(cM01Get.ExplicitInterfaceImplementations); + } + + Assert.Empty(cM01Set.ExplicitInterfaceImplementations); + } + + var source4 = +@" +public class C1 : I1 +{ + static int I1.M01 { get; set; } +} +"; + + var compilation4 = CreateCompilationWithIL(source4, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation4.VerifyDiagnostics( + // (4,29): error CS0550: 'C1.I1.M01.set' adds an accessor not found in interface member 'I1.M01' + // static int I1.M01 { get; set; } + Diagnostic(ErrorCode.ERR_ExplicitPropertyAddingAccessor, "set").WithArguments("C1.I1.M01.set", "I1.M01").WithLocation(4, 29) + ); + + c1 = compilation4.GlobalNamespace.GetTypeMember("C1"); + i1 = c1.Interfaces().Single(); + m01 = i1.GetMembers().OfType().Single(); + var c1M01 = c1.GetMembers().OfType().Single(); + + Assert.Same(c1M01, c1.FindImplementationForInterfaceMember(m01)); + Assert.Same(c1M01.GetMethod, c1.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.SetMethod)); + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.GetMethod, c1M01.GetMethod.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.SetMethod, c1M01.SetMethod.ExplicitInterfaceImplementations.Single()); + + var source5 = +@" +public class C1 : I1 +{ + public static int M01 { get; } +} +"; + + var compilation5 = CreateCompilationWithIL(source5, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation5, sourceSymbolValidator: validate5, symbolValidator: validate5, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate5(ModuleSymbol module) + { + var c = module.GlobalNamespace.GetTypeMember("C1"); + + var m01 = c.Interfaces().Single().GetMembers().OfType().Single(); + var m01Get = m01.GetMethod; + + Assert.Equal(1, c.GetMembers().OfType().Count()); + Assert.Equal(1, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var cM01 = (PropertySymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + + Assert.Equal("System.Int32 C1.M01 { get; }", cM01.ToTestDisplayString()); + + var cM01Get = cM01.GetMethod; + Assert.Same(cM01Get, c.FindImplementationForInterfaceMember(m01Get)); + + Assert.True(cM01Get.IsStatic); + Assert.False(cM01Get.IsAbstract); + Assert.False(cM01Get.IsVirtual); + Assert.False(cM01Get.IsMetadataVirtual()); + Assert.False(cM01Get.IsMetadataFinal); + Assert.False(cM01Get.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertyGet, cM01Get.MethodKind); + + Assert.Equal("System.Int32 C1.M01.get", cM01Get.ToTestDisplayString()); + + Assert.Null(c.FindImplementationForInterfaceMember(m01.SetMethod)); + + if (module is PEModuleSymbol) + { + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01Get, cM01Get.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(cM01.ExplicitInterfaceImplementations); + Assert.Empty(cM01Get.ExplicitInterfaceImplementations); + } + } + + var source6 = +@" +public class C1 : I1 +{ + public static int M01 { set{} } +} +"; + + var compilation6 = CreateCompilationWithIL(source6, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation6.VerifyDiagnostics( + // (2,19): error CS0535: 'C1' does not implement interface member 'I1.M01.get' + // public class C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01.get").WithLocation(2, 19) + ); + + c1 = compilation6.GlobalNamespace.GetTypeMember("C1"); + i1 = c1.Interfaces().Single(); + m01 = i1.GetMembers().OfType().Single(); + c1M01 = c1.GetMembers().OfType().Single(); + + Assert.Same(c1M01, c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.SetMethod)); + + var source7 = +@" +public class C1 : I1 +{ + static int I1.M01 { set{} } +} +"; + + var compilation7 = CreateCompilationWithIL(source7, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation7.VerifyDiagnostics( + // (2,19): error CS0535: 'C1' does not implement interface member 'I1.M01.get' + // public class C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01.get").WithLocation(2, 19), + // (4,18): error CS0551: Explicit interface implementation 'C1.I1.M01' is missing accessor 'I1.M01.get' + // static int I1.M01 { set{} } + Diagnostic(ErrorCode.ERR_ExplicitPropertyMissingAccessor, "M01").WithArguments("C1.I1.M01", "I1.M01.get").WithLocation(4, 18), + // (4,24): error CS0550: 'C1.I1.M01.set' adds an accessor not found in interface member 'I1.M01' + // static int I1.M01 { set{} } + Diagnostic(ErrorCode.ERR_ExplicitPropertyAddingAccessor, "set").WithArguments("C1.I1.M01.set", "I1.M01").WithLocation(4, 24) + ); + + c1 = compilation7.GlobalNamespace.GetTypeMember("C1"); + i1 = c1.Interfaces().Single(); + m01 = i1.GetMembers().OfType().Single(); + c1M01 = c1.GetMembers().OfType().Single(); + + Assert.Same(c1M01, c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.SetMethod)); + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.SetMethod, c1M01.SetMethod.ExplicitInterfaceImplementations.Single()); + } + + void scenario3() + { + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method private hidebysig specialname static virtual + int32 get_M01 () cil managed + { + IL_0000: ldc.i4.0 + IL_0001: ret + } + + .method public hidebysig specialname abstract virtual static + void set_M01 ( + int32 'value' + ) cil managed + { + } + + .property int32 M01() + { + .get int32 I1::get_M01() + .set void I1::set_M01(int32) + } +} +"; + + var source1 = +@" +public class C1 : I1 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (2,19): error CS0535: 'C1' does not implement interface member 'I1.M01' + // public class C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01").WithLocation(2, 19) + ); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var i1 = c1.Interfaces().Single(); + var m01 = i1.GetMembers().OfType().Single(); + + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.SetMethod)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01.SetMethod)); + + var source2 = +@" +public class C1 : I1 +{ + static int I1.M01 { set{} } +} +"; + + var compilation2 = CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate2, symbolValidator: validate2, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate2(ModuleSymbol module) + { + var c = module.GlobalNamespace.GetTypeMember("C1"); + var m01 = c.Interfaces().Single().GetMembers().OfType().Single(); + var m01Get = m01.GetMethod; + var m01Set = m01.SetMethod; + + Assert.Equal(1, c.GetMembers().OfType().Count()); + Assert.Equal(1, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var cM01 = (PropertySymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + + Assert.Equal("System.Int32 C1.I1.M01 { set; }", cM01.ToTestDisplayString()); + + var cM01Set = cM01.SetMethod; + Assert.Same(cM01Set, c.FindImplementationForInterfaceMember(m01Set)); + + Assert.True(cM01Set.IsStatic); + Assert.False(cM01Set.IsAbstract); + Assert.False(cM01Set.IsVirtual); + Assert.False(cM01Set.IsMetadataVirtual()); + Assert.False(cM01Set.IsMetadataFinal); + Assert.False(cM01Set.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertySet, cM01Set.MethodKind); + + Assert.Equal("void C1.I1.M01.set", cM01Set.ToTestDisplayString()); + + Assert.Null(cM01.GetMethod); + Assert.Null(c.FindImplementationForInterfaceMember(m01Get)); + + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01Set, cM01Set.ExplicitInterfaceImplementations.Single()); + } + + var source3 = +@" +public class C1 : I1 +{ + public static int M01 { get; set; } +} +"; + + var compilation3 = CreateCompilationWithIL(source3, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation3, sourceSymbolValidator: validate3, symbolValidator: validate3, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate3(ModuleSymbol module) + { + var c = module.GlobalNamespace.GetTypeMember("C1"); + + var m01 = c.Interfaces().Single().GetMembers().OfType().Single(); + var m01Set = m01.SetMethod; + + Assert.Equal(1, c.GetMembers().OfType().Count()); + Assert.Equal(2, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var cM01 = (PropertySymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + + Assert.Equal("System.Int32 C1.M01 { get; set; }", cM01.ToTestDisplayString()); + + var cM01Set = cM01.SetMethod; + Assert.Same(cM01Set, c.FindImplementationForInterfaceMember(m01Set)); + + Assert.True(cM01Set.IsStatic); + Assert.False(cM01Set.IsAbstract); + Assert.False(cM01Set.IsVirtual); + Assert.False(cM01Set.IsMetadataVirtual()); + Assert.False(cM01Set.IsMetadataFinal); + Assert.False(cM01Set.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertySet, cM01Set.MethodKind); + + Assert.Equal("void C1.M01.set", cM01Set.ToTestDisplayString()); + + var cM01Get = cM01.GetMethod; + + Assert.True(cM01Get.IsStatic); + Assert.False(cM01Get.IsAbstract); + Assert.False(cM01Get.IsVirtual); + Assert.False(cM01Get.IsMetadataVirtual()); + Assert.False(cM01Get.IsMetadataFinal); + Assert.False(cM01Get.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertyGet, cM01Get.MethodKind); + + Assert.Equal("System.Int32 C1.M01.get", cM01Get.ToTestDisplayString()); + + Assert.Null(c.FindImplementationForInterfaceMember(m01.GetMethod)); + + if (module is PEModuleSymbol) + { + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01Set, cM01Set.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(cM01.ExplicitInterfaceImplementations); + Assert.Empty(cM01Set.ExplicitInterfaceImplementations); + } + + Assert.Empty(cM01Get.ExplicitInterfaceImplementations); + } + + var source4 = +@" +public class C1 : I1 +{ + static int I1.M01 { get; set; } +} +"; + + var compilation4 = CreateCompilationWithIL(source4, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation4.VerifyDiagnostics( + // (4,24): error CS0550: 'C1.I1.M01.get' adds an accessor not found in interface member 'I1.M01' + // static int I1.M01 { get; set; } + Diagnostic(ErrorCode.ERR_ExplicitPropertyAddingAccessor, "get").WithArguments("C1.I1.M01.get", "I1.M01").WithLocation(4, 24) + ); + + c1 = compilation4.GlobalNamespace.GetTypeMember("C1"); + i1 = c1.Interfaces().Single(); + m01 = i1.GetMembers().OfType().Single(); + var c1M01 = c1.GetMembers().OfType().Single(); + + Assert.Same(c1M01, c1.FindImplementationForInterfaceMember(m01)); + Assert.Same(c1M01.SetMethod, c1.FindImplementationForInterfaceMember(m01.SetMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.GetMethod, c1M01.GetMethod.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.SetMethod, c1M01.SetMethod.ExplicitInterfaceImplementations.Single()); + + var source5 = +@" +public class C1 : I1 +{ + public static int M01 { set{} } +} +"; + + var compilation5 = CreateCompilationWithIL(source5, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation5, sourceSymbolValidator: validate5, symbolValidator: validate5, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate5(ModuleSymbol module) + { + var c = module.GlobalNamespace.GetTypeMember("C1"); + + var m01 = c.Interfaces().Single().GetMembers().OfType().Single(); + var m01Set = m01.SetMethod; + + Assert.Equal(1, c.GetMembers().OfType().Count()); + Assert.Equal(1, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var cM01 = (PropertySymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + + Assert.Equal("System.Int32 C1.M01 { set; }", cM01.ToTestDisplayString()); + + var cM01Set = cM01.SetMethod; + Assert.Same(cM01Set, c.FindImplementationForInterfaceMember(m01Set)); + + Assert.True(cM01Set.IsStatic); + Assert.False(cM01Set.IsAbstract); + Assert.False(cM01Set.IsVirtual); + Assert.False(cM01Set.IsMetadataVirtual()); + Assert.False(cM01Set.IsMetadataFinal); + Assert.False(cM01Set.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertySet, cM01Set.MethodKind); + + Assert.Equal("void C1.M01.set", cM01Set.ToTestDisplayString()); + + Assert.Null(c.FindImplementationForInterfaceMember(m01.GetMethod)); + + if (module is PEModuleSymbol) + { + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01Set, cM01Set.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(cM01.ExplicitInterfaceImplementations); + Assert.Empty(cM01Set.ExplicitInterfaceImplementations); + } + } + + var source6 = +@" +public class C1 : I1 +{ + public static int M01 { get; } +} +"; + + var compilation6 = CreateCompilationWithIL(source6, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation6.VerifyDiagnostics( + // (2,19): error CS0535: 'C1' does not implement interface member 'I1.M01.set' + // public class C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01.set").WithLocation(2, 19) + ); + + c1 = compilation6.GlobalNamespace.GetTypeMember("C1"); + i1 = c1.Interfaces().Single(); + m01 = i1.GetMembers().OfType().Single(); + c1M01 = c1.GetMembers().OfType().Single(); + + Assert.Same(c1M01, c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.SetMethod)); + + var source7 = +@" +public class C1 : I1 +{ + static int I1.M01 { get; } +} +"; + + var compilation7 = CreateCompilationWithIL(source7, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation7.VerifyDiagnostics( + // (2,19): error CS0535: 'C1' does not implement interface member 'I1.M01.set' + // public class C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01.set").WithLocation(2, 19), + // (4,18): error CS0551: Explicit interface implementation 'C1.I1.M01' is missing accessor 'I1.M01.set' + // static int I1.M01 { get; } + Diagnostic(ErrorCode.ERR_ExplicitPropertyMissingAccessor, "M01").WithArguments("C1.I1.M01", "I1.M01.set").WithLocation(4, 18), + // (4,24): error CS0550: 'C1.I1.M01.get' adds an accessor not found in interface member 'I1.M01' + // static int I1.M01 { get; } + Diagnostic(ErrorCode.ERR_ExplicitPropertyAddingAccessor, "get").WithArguments("C1.I1.M01.get", "I1.M01").WithLocation(4, 24) + ); + + c1 = compilation7.GlobalNamespace.GetTypeMember("C1"); + i1 = c1.Interfaces().Single(); + m01 = i1.GetMembers().OfType().Single(); + c1M01 = c1.GetMembers().OfType().Single(); + + Assert.Same(c1M01, c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.SetMethod)); + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.GetMethod, c1M01.GetMethod.ExplicitInterfaceImplementations.Single()); + } + } + + [Fact] + public void ImplementAbstractStaticProperty_12() + { + // Ignore invalid metadata (default interface implementation for a static method) + + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig specialname abstract virtual static + int32 get_M01 () cil managed + { + } + + .method public hidebysig specialname abstract virtual static + void set_M01 ( + int32 'value' + ) cil managed + { + } + + .property int32 M01() + { + .get int32 I1::get_M01() + .set void I1::set_M01(int32) + } +} + +.class interface public auto ansi abstract I2 + implements I1 +{ + .method private hidebysig specialname static + int32 I1.get_M01 () cil managed + { + .override method int32 I1::get_M01() + IL_0000: ldc.i4.0 + IL_0001: ret + } + + .method private hidebysig specialname static + void I1.set_M01 ( + int32 'value' + ) cil managed + { + .override method void I1::set_M01(int32) + IL_0000: ret + } + + .property instance int32 I1.M01() + { + .get int32 I2::I1.get_M01() + .set void I2::I1.set_M01(int32) + } +} +"; + + var source1 = +@" +public class C1 : I2 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyEmitDiagnostics( + // (2,19): error CS0535: 'C1' does not implement interface member 'I1.M01' + // public class C1 : I2 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I2").WithArguments("C1", "I1.M01").WithLocation(2, 19) + ); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var i2 = c1.Interfaces().Single(); + var i1 = i2.Interfaces().Single(); + var m01 = i1.GetMembers().OfType().Single(); + + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(i2.FindImplementationForInterfaceMember(m01)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Null(i2.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.SetMethod)); + Assert.Null(i2.FindImplementationForInterfaceMember(m01.SetMethod)); + + var i2M01 = i2.GetMembers().OfType().Single(); + Assert.Same(m01, i2M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.GetMethod, i2M01.GetMethod.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.SetMethod, i2M01.SetMethod.ExplicitInterfaceImplementations.Single()); + } + + [Fact] + public void ImplementAbstractStaticProperty_13() + { + // A forwarding method is added for an implicit implementation declared in base class. + + var source1 = +@" +public interface I1 +{ + abstract static int M01 { get; set; } +} + +class C1 +{ + public static int M01 { get; set; } +} + +class C2 : C1, I1 +{ +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var m01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + + var c2M01 = (PropertySymbol)c2.FindImplementationForInterfaceMember(m01); + var c2M01Get = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01.GetMethod); + var c2M01Set = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01.SetMethod); + + Assert.True(c2M01Get.IsStatic); + Assert.False(c2M01Get.IsAbstract); + Assert.False(c2M01Get.IsVirtual); + Assert.False(c2M01Get.IsMetadataVirtual()); + Assert.False(c2M01Get.IsMetadataFinal); + Assert.False(c2M01Get.IsMetadataNewSlot()); + + Assert.True(c2M01Set.IsStatic); + Assert.False(c2M01Set.IsAbstract); + Assert.False(c2M01Set.IsVirtual); + Assert.False(c2M01Set.IsMetadataVirtual()); + Assert.False(c2M01Set.IsMetadataFinal); + Assert.False(c2M01Set.IsMetadataNewSlot()); + + if (module is PEModuleSymbol) + { + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c2M01Get.MethodKind); + Assert.False(c2M01Get.HasRuntimeSpecialName); + Assert.False(c2M01Get.HasSpecialName); + Assert.Equal("System.Int32 C2.I1.get_M01()", c2M01Get.ToTestDisplayString()); + Assert.Same(m01.GetMethod, c2M01Get.ExplicitInterfaceImplementations.Single()); + + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c2M01Set.MethodKind); + Assert.False(c2M01Set.HasRuntimeSpecialName); + Assert.False(c2M01Set.HasSpecialName); + Assert.Equal("void C2.I1.set_M01(System.Int32 value)", c2M01Set.ToTestDisplayString()); + Assert.Same(m01.SetMethod, c2M01Set.ExplicitInterfaceImplementations.Single()); + + // Forwarding methods for accessors aren't tied to a property + Assert.Null(c2M01); + + var c1M01 = module.GlobalNamespace.GetMember("C1.M01"); + var c1M01Get = c1M01.GetMethod; + var c1M01Set = c1M01.SetMethod; + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + + Assert.True(c1M01Get.IsStatic); + Assert.False(c1M01Get.IsAbstract); + Assert.False(c1M01Get.IsVirtual); + Assert.False(c1M01Get.IsMetadataVirtual()); + Assert.False(c1M01Get.IsMetadataFinal); + Assert.False(c1M01Get.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertyGet, c1M01Get.MethodKind); + Assert.False(c1M01Get.HasRuntimeSpecialName); + Assert.True(c1M01Get.HasSpecialName); + Assert.Empty(c1M01Get.ExplicitInterfaceImplementations); + + Assert.True(c1M01Set.IsStatic); + Assert.False(c1M01Set.IsAbstract); + Assert.False(c1M01Set.IsVirtual); + Assert.False(c1M01Set.IsMetadataVirtual()); + Assert.False(c1M01Set.IsMetadataFinal); + Assert.False(c1M01Set.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertySet, c1M01Set.MethodKind); + Assert.False(c1M01Set.HasRuntimeSpecialName); + Assert.True(c1M01Set.HasSpecialName); + Assert.Empty(c1M01Set.ExplicitInterfaceImplementations); + } + else + { + Assert.True(c2M01.IsStatic); + Assert.False(c2M01.IsAbstract); + Assert.False(c2M01.IsVirtual); + + Assert.Equal("System.Int32 C1.M01 { get; set; }", c2M01.ToTestDisplayString()); + Assert.Empty(c2M01.ExplicitInterfaceImplementations); + + Assert.Equal(MethodKind.PropertyGet, c2M01Get.MethodKind); + Assert.False(c2M01Get.HasRuntimeSpecialName); + Assert.True(c2M01Get.HasSpecialName); + Assert.Same(c2M01.GetMethod, c2M01Get); + Assert.Empty(c2M01Get.ExplicitInterfaceImplementations); + + Assert.Equal(MethodKind.PropertySet, c2M01Set.MethodKind); + Assert.False(c2M01Set.HasRuntimeSpecialName); + Assert.True(c2M01Set.HasSpecialName); + Assert.Same(c2M01.SetMethod, c2M01Set); + Assert.Empty(c2M01Set.ExplicitInterfaceImplementations); + } + } + + verifier.VerifyIL("C2.I1.get_M01", +@" +{ + // Code size 6 (0x6) + .maxstack 1 + IL_0000: call ""int C1.M01.get"" + IL_0005: ret +} +"); + + verifier.VerifyIL("C2.I1.set_M01", +@" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""void C1.M01.set"" + IL_0006: ret +} +"); + } + + [Fact] + public void ImplementAbstractStaticProperty_14() + { + // A forwarding method is added for an implicit implementation with modopt mismatch. + + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig specialname abstract virtual static + int32 get_M01 () cil managed + { + } + + .method public hidebysig specialname abstract virtual static + void modopt(I1) set_M01 ( + int32 'value' + ) cil managed + { + } + + .property int32 M01() + { + .get int32 I1::get_M01() + .set void modopt(I1) I1::set_M01(int32) + } +} + +.class interface public auto ansi abstract I2 +{ + .method public hidebysig specialname abstract virtual static + int32 modopt(I2) get_M01 () cil managed + { + } + + .method public hidebysig specialname abstract virtual static + void set_M01 ( + int32 modopt(I2) 'value' + ) cil managed + { + } + + .property int32 modopt(I2) M01() + { + .get int32 modopt(I2) I2::get_M01() + .set void I2::set_M01(int32 modopt(I2)) + } +} +"; + + var source1 = +@" +class C1 : I1 +{ + public static int M01 { get; set; } +} + +class C2 : I1 +{ + static int I1.M01 { get; set; } +} + +class C3 : I2 +{ + static int I2.M01 { get; set; } +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c1 = module.GlobalNamespace.GetTypeMember("C1"); + var m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); + + var c1M01 = (PropertySymbol)c1.FindImplementationForInterfaceMember(m01); + var c1M01Get = c1M01.GetMethod; + var c1M01Set = c1M01.SetMethod; + + Assert.Equal("System.Int32 C1.M01 { get; set; }", c1M01.ToTestDisplayString()); + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + + Assert.Equal(MethodKind.PropertyGet, c1M01Get.MethodKind); + Assert.Equal("System.Int32 C1.M01.get", c1M01Get.ToTestDisplayString()); + Assert.True(c1M01Get.IsStatic); + Assert.False(c1M01Get.IsAbstract); + Assert.False(c1M01Get.IsVirtual); + Assert.False(c1M01Get.IsMetadataVirtual()); + Assert.False(c1M01Get.IsMetadataFinal); + Assert.False(c1M01Get.IsMetadataNewSlot()); + Assert.Same(c1M01Get, c1.FindImplementationForInterfaceMember(m01.GetMethod)); + + Assert.Equal(MethodKind.PropertySet, c1M01Set.MethodKind); + Assert.Equal("void C1.M01.set", c1M01Set.ToTestDisplayString()); + Assert.Empty(c1M01Set.ExplicitInterfaceImplementations); + Assert.True(c1M01Set.IsStatic); + Assert.False(c1M01Set.IsAbstract); + Assert.False(c1M01Set.IsVirtual); + Assert.False(c1M01Set.IsMetadataVirtual()); + Assert.False(c1M01Set.IsMetadataFinal); + Assert.False(c1M01Set.IsMetadataNewSlot()); + + if (module is PEModuleSymbol) + { + Assert.Same(m01.GetMethod, c1M01Get.ExplicitInterfaceImplementations.Single()); + + c1M01Set = (MethodSymbol)c1.FindImplementationForInterfaceMember(m01.SetMethod); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c1M01Set.MethodKind); + Assert.Equal("void modopt(I1) C1.I1.set_M01(System.Int32 value)", c1M01Set.ToTestDisplayString()); + Assert.Same(m01.SetMethod, c1M01Set.ExplicitInterfaceImplementations.Single()); + + Assert.True(c1M01Set.IsStatic); + Assert.False(c1M01Set.IsAbstract); + Assert.False(c1M01Set.IsVirtual); + Assert.False(c1M01Set.IsMetadataVirtual()); + Assert.False(c1M01Set.IsMetadataFinal); + Assert.False(c1M01Set.IsMetadataNewSlot()); + } + else + { + Assert.Empty(c1M01Get.ExplicitInterfaceImplementations); + Assert.Same(c1M01Set, c1.FindImplementationForInterfaceMember(m01.SetMethod)); + } + + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + + var c2M01 = (PropertySymbol)c2.FindImplementationForInterfaceMember(m01); + var c2M01Get = c2M01.GetMethod; + var c2M01Set = c2M01.SetMethod; + + Assert.Equal("System.Int32 C2.I1.M01 { get; set; }", c2M01.ToTestDisplayString()); + + Assert.True(c2M01.IsStatic); + Assert.False(c2M01.IsAbstract); + Assert.False(c2M01.IsVirtual); + Assert.Same(m01, c2M01.ExplicitInterfaceImplementations.Single()); + + Assert.True(c2M01Get.IsStatic); + Assert.False(c2M01Get.IsAbstract); + Assert.False(c2M01Get.IsVirtual); + Assert.False(c2M01Get.IsMetadataVirtual()); + Assert.False(c2M01Get.IsMetadataFinal); + Assert.False(c2M01Get.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertyGet, c2M01Get.MethodKind); + Assert.Equal("System.Int32 C2.I1.M01.get", c2M01Get.ToTestDisplayString()); + Assert.Same(m01.GetMethod, c2M01Get.ExplicitInterfaceImplementations.Single()); + Assert.Same(c2M01Get, c2.FindImplementationForInterfaceMember(m01.GetMethod)); + + Assert.True(c2M01Set.IsStatic); + Assert.False(c2M01Set.IsAbstract); + Assert.False(c2M01Set.IsVirtual); + Assert.False(c2M01Set.IsMetadataVirtual()); + Assert.False(c2M01Set.IsMetadataFinal); + Assert.False(c2M01Set.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertySet, c2M01Set.MethodKind); + Assert.Equal("void modopt(I1) C2.I1.M01.set", c2M01Set.ToTestDisplayString()); + Assert.Same(m01.SetMethod, c2M01Set.ExplicitInterfaceImplementations.Single()); + Assert.Same(c2M01Set, c2.FindImplementationForInterfaceMember(m01.SetMethod)); + + Assert.Same(c2M01, c2.GetMembers().OfType().Single()); + Assert.Equal(2, c2.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + m01 = c3.Interfaces().Single().GetMembers().OfType().Single(); + + var c3M01 = (PropertySymbol)c3.FindImplementationForInterfaceMember(m01); + var c3M01Get = c3M01.GetMethod; + var c3M01Set = c3M01.SetMethod; + + Assert.Equal("System.Int32 modopt(I2) C3.I2.M01 { get; set; }", c3M01.ToTestDisplayString()); + + Assert.True(c3M01.IsStatic); + Assert.False(c3M01.IsAbstract); + Assert.False(c3M01.IsVirtual); + Assert.Same(m01, c3M01.ExplicitInterfaceImplementations.Single()); + + Assert.True(c3M01Get.IsStatic); + Assert.False(c3M01Get.IsAbstract); + Assert.False(c3M01Get.IsVirtual); + Assert.False(c3M01Get.IsMetadataVirtual()); + Assert.False(c3M01Get.IsMetadataFinal); + Assert.False(c3M01Get.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertyGet, c3M01Get.MethodKind); + Assert.Equal("System.Int32 modopt(I2) C3.I2.M01.get", c3M01Get.ToTestDisplayString()); + Assert.Same(m01.GetMethod, c3M01Get.ExplicitInterfaceImplementations.Single()); + Assert.Same(c3M01Get, c3.FindImplementationForInterfaceMember(m01.GetMethod)); + + + Assert.True(c3M01Set.IsStatic); + Assert.False(c3M01Set.IsAbstract); + Assert.False(c3M01Set.IsVirtual); + Assert.False(c3M01Set.IsMetadataVirtual()); + Assert.False(c3M01Set.IsMetadataFinal); + Assert.False(c3M01Set.IsMetadataNewSlot()); + Assert.Equal(MethodKind.PropertySet, c3M01Set.MethodKind); + Assert.Equal("void C3.I2.M01.set", c3M01Set.ToTestDisplayString()); + Assert.Equal("System.Int32 modopt(I2) value", c3M01Set.Parameters.Single().ToTestDisplayString()); + Assert.Same(m01.SetMethod, c3M01Set.ExplicitInterfaceImplementations.Single()); + Assert.Same(c3M01Set, c3.FindImplementationForInterfaceMember(m01.SetMethod)); + + Assert.Same(c3M01, c3.GetMembers().OfType().Single()); + Assert.Equal(2, c3.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + } + + verifier.VerifyIL("C1.I1.set_M01", +@" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""void C1.M01.set"" + IL_0006: ret +} +"); + } + + [Fact] + public void ImplementAbstractStatiProperty_15() + { + // A forwarding method isn't created if base class implements interface exactly the same way. + + var source1 = +@" +public interface I1 +{ + abstract static int M01 { get; set; } + abstract static int M02 { get; set; } +} + +public class C1 +{ + public static int M01 { get; set; } +} + +public class C2 : C1, I1 +{ + static int I1.M02 { get; set; } +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C3 : C2, I1 +{ +} +"; + + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.RegularPreview, TestOptions.Regular9 }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } + + void validate(ModuleSymbol module) + { + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + Assert.Empty(c3.GetMembers().OfType()); + Assert.Empty(c3.GetMembers().OfType().Where(m => !m.IsConstructor())); + + var m01 = c3.Interfaces().Single().GetMembers("M01").OfType().Single(); + + var c1M01 = c3.BaseType().BaseType().GetMember("M01"); + Assert.Equal("System.Int32 C1.M01 { get; set; }", c1M01.ToTestDisplayString()); + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + + var c1M01Get = c1M01.GetMethod; + Assert.True(c1M01Get.IsStatic); + Assert.False(c1M01Get.IsAbstract); + Assert.False(c1M01Get.IsVirtual); + Assert.False(c1M01Get.IsMetadataVirtual()); + Assert.False(c1M01Get.IsMetadataFinal); + Assert.False(c1M01Get.IsMetadataNewSlot()); + + Assert.Empty(c1M01Get.ExplicitInterfaceImplementations); + + var c1M01Set = c1M01.SetMethod; + Assert.True(c1M01Set.IsStatic); + Assert.False(c1M01Set.IsAbstract); + Assert.False(c1M01Set.IsVirtual); + Assert.False(c1M01Set.IsMetadataVirtual()); + Assert.False(c1M01Set.IsMetadataFinal); + Assert.False(c1M01Set.IsMetadataNewSlot()); + + Assert.Empty(c1M01Set.ExplicitInterfaceImplementations); + + if (c1M01.ContainingModule is PEModuleSymbol) + { + var c2M01Get = c3.FindImplementationForInterfaceMember(m01.GetMethod); + Assert.Equal("System.Int32 C2.I1.get_M01()", c2M01Get.ToTestDisplayString()); + + var c2M01Set = c3.FindImplementationForInterfaceMember(m01.SetMethod); + Assert.Equal("void C2.I1.set_M01(System.Int32 value)", c2M01Set.ToTestDisplayString()); + + // Forwarding methods for accessors aren't tied to a property + Assert.Null(c3.FindImplementationForInterfaceMember(m01)); + } + else + { + Assert.Same(c1M01, c3.FindImplementationForInterfaceMember(m01)); + Assert.Same(c1M01.GetMethod, c3.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Same(c1M01.SetMethod, c3.FindImplementationForInterfaceMember(m01.SetMethod)); + } + + var m02 = c3.Interfaces().Single().GetMembers("M02").OfType().Single(); + + var c2M02 = c3.BaseType().GetMember("I1.M02"); + Assert.Equal("System.Int32 C2.I1.M02 { get; set; }", c2M02.ToTestDisplayString()); + Assert.Same(c2M02, c3.FindImplementationForInterfaceMember(m02)); + Assert.Same(c2M02.GetMethod, c3.FindImplementationForInterfaceMember(m02.GetMethod)); + Assert.Same(c2M02.SetMethod, c3.FindImplementationForInterfaceMember(m02.SetMethod)); + } + } + + [Fact] + public void ImplementAbstractStaticProperty_16() + { + // A new implicit implementation is properly considered. + + var source1 = +@" +public interface I1 +{ + abstract static int M01 { get; set; } +} + +public class C1 : I1 +{ + public static int M01 { get; set; } +} + +public class C2 : C1 +{ + new public static int M01 { get; set; } +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C3 : C2, I1 +{ +} +"; + + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + var verifier = CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("C3.I1.get_M01", +@" +{ + // Code size 6 (0x6) + .maxstack 1 + IL_0000: call ""int C2.M01.get"" + IL_0005: ret +} +"); + + verifier.VerifyIL("C3.I1.set_M01", +@" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""void C2.M01.set"" + IL_0006: ret +} +"); + } + } + + void validate(ModuleSymbol module) + { + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + var m01 = c3.Interfaces().Single().GetMembers().OfType().Single(); + + var c2M01 = c3.BaseType().GetMember("M01"); + var c2M01Get = c2M01.GetMethod; + var c2M01Set = c2M01.SetMethod; + Assert.Equal("System.Int32 C2.M01 { get; set; }", c2M01.ToTestDisplayString()); + + Assert.True(c2M01.IsStatic); + Assert.False(c2M01.IsAbstract); + Assert.False(c2M01.IsVirtual); + Assert.Empty(c2M01.ExplicitInterfaceImplementations); + + Assert.True(c2M01Get.IsStatic); + Assert.False(c2M01Get.IsAbstract); + Assert.False(c2M01Get.IsVirtual); + Assert.False(c2M01Get.IsMetadataVirtual()); + Assert.False(c2M01Get.IsMetadataFinal); + Assert.False(c2M01Get.IsMetadataNewSlot()); + Assert.Empty(c2M01Get.ExplicitInterfaceImplementations); + + Assert.True(c2M01Set.IsStatic); + Assert.False(c2M01Set.IsAbstract); + Assert.False(c2M01Set.IsVirtual); + Assert.False(c2M01Set.IsMetadataVirtual()); + Assert.False(c2M01Set.IsMetadataFinal); + Assert.False(c2M01Set.IsMetadataNewSlot()); + Assert.Empty(c2M01Set.ExplicitInterfaceImplementations); + + if (module is PEModuleSymbol) + { + var c3M01 = (PropertySymbol)c3.FindImplementationForInterfaceMember(m01); + // Forwarding methods for accessors aren't tied to a property + Assert.Null(c3M01); + + var c3M01Get = (MethodSymbol)c3.FindImplementationForInterfaceMember(m01.GetMethod); + Assert.Equal("System.Int32 C3.I1.get_M01()", c3M01Get.ToTestDisplayString()); + Assert.Same(m01.GetMethod, c3M01Get.ExplicitInterfaceImplementations.Single()); + + var c3M01Set = (MethodSymbol)c3.FindImplementationForInterfaceMember(m01.SetMethod); + Assert.Equal("void C3.I1.set_M01(System.Int32 value)", c3M01Set.ToTestDisplayString()); + Assert.Same(m01.SetMethod, c3M01Set.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Same(c2M01, c3.FindImplementationForInterfaceMember(m01)); + Assert.Same(c2M01Get, c3.FindImplementationForInterfaceMember(m01.GetMethod)); + Assert.Same(c2M01Set, c3.FindImplementationForInterfaceMember(m01.SetMethod)); + } + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticProperty_19(bool genericFirst) + { + // An "ambiguity" in implicit/explicit implementation declared in generic base class. + + var generic = +@" + public static T M01 { get; set; } +"; + var nonGeneric = +@" + static int I1.M01 { get; set; } +"; + var source1 = +@" +public interface I1 +{ + abstract static int M01 { get; set; } +} + +public class C1 : I1 +{ +" + (genericFirst ? generic + nonGeneric : nonGeneric + generic) + @" +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { CreateCompilation("", targetFramework: TargetFramework.NetCoreApp).ToMetadataReference() }); + + Assert.Equal(2, compilation1.GlobalNamespace.GetTypeMember("C1").GetMembers().OfType().Where(m => m.Name.Contains("M01")).Count()); + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C2 : C1, I1 +{ +} +"; + + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } + + void validate(ModuleSymbol module) + { + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + var m01 = c2.Interfaces().Single().GetMembers().OfType().Single(); + + Assert.True(m01.ContainingModule is RetargetingModuleSymbol or PEModuleSymbol); + + var c1M01 = (PropertySymbol)c2.FindImplementationForInterfaceMember(m01); + Assert.Equal("System.Int32 C1.I1.M01 { get; set; }", c1M01.OriginalDefinition.ToTestDisplayString()); + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(c1M01, c2.BaseType().FindImplementationForInterfaceMember(m01)); + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticProperty_20(bool genericFirst) + { + // Same as ImplementAbstractStaticProperty_19 only interface is generic too. + + var generic = +@" + static T I1.M01 { get; set; } +"; + var nonGeneric = +@" + public static int M01 { get; set; } +"; + var source1 = +@" +public interface I1 +{ + abstract static T M01 { get; set; } +} + +public class C1 : I1 +{ +" + (genericFirst ? generic + nonGeneric : nonGeneric + generic) + @" +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { CreateCompilation("", targetFramework: TargetFramework.NetCoreApp).ToMetadataReference() }); + + Assert.Equal(2, compilation1.GlobalNamespace.GetTypeMember("C1").GetMembers().OfType().Where(m => m.Name.Contains("M01")).Count()); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C2 : C1, I1 +{ +} +"; + + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } + + void validate(ModuleSymbol module) + { + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + var m01 = c2.Interfaces().Single().GetMembers().OfType().Single(); + + Assert.True(m01.ContainingModule is RetargetingModuleSymbol or PEModuleSymbol); + + var c1M01 = (PropertySymbol)c2.FindImplementationForInterfaceMember(m01); + Assert.Equal("T C1.I1.M01 { get; set; }", c1M01.OriginalDefinition.ToTestDisplayString()); + Assert.Equal(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(c1M01, c2.BaseType().FindImplementationForInterfaceMember(m01)); + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticEvent_01(bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@"#pragma warning disable CS0067 // WRN_UnreferencedEvent +public interface I1 +{ + abstract static event System.Action M01; +} + +" + typeKeyword + @" + C1 : I1 +{} + +" + typeKeyword + @" + C2 : I1 +{ + public event System.Action M01; +} + +" + typeKeyword + @" + C3 : I1 +{ + static event System.Action M01; +} + +" + typeKeyword + @" + C4 : I1 +{ + event System.Action I1.M01 { add{} remove{}} +} + +" + typeKeyword + @" + C5 : I1 +{ + public static event System.Action M01; +} + +" + typeKeyword + @" + C6 : I1 +{ + static event System.Action I1.M01 { add{} remove{}} +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (8,10): error CS0535: 'C1' does not implement interface member 'I1.M01' + // C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01").WithLocation(8, 10), + // (12,10): error CS9109: 'C2' does not implement static interface member 'I1.M01'. 'C2.M01' cannot implement the interface member because it is not static. + // C2 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotStatic, "I1").WithArguments("C2", "I1.M01", "C2.M01").WithLocation(12, 10), + // (18,10): error CS0737: 'C3' does not implement interface member 'I1.M01'. 'C3.M01' cannot implement an interface member because it is not public. + // C3 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotPublic, "I1").WithArguments("C3", "I1.M01", "C3.M01").WithLocation(18, 10), + // (24,10): error CS0535: 'C4' does not implement interface member 'I1.M01' + // C4 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C4", "I1.M01").WithLocation(24, 10), + // (26,28): error CS0539: 'C4.M01' in explicit interface declaration is not found among members of the interface that can be implemented + // event System.Action I1.M01 { add{} remove{}} + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("C4.M01").WithLocation(26, 28), + // (30,10): error CS0738: 'C5' does not implement interface member 'I1.M01'. 'C5.M01' cannot implement 'I1.M01' because it does not have the matching return type of 'Action'. + // C5 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberWrongReturnType, "I1").WithArguments("C5", "I1.M01", "C5.M01", "System.Action").WithLocation(30, 10), + // (36,10): error CS0535: 'C6' does not implement interface member 'I1.M01' + // C6 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C6", "I1.M01").WithLocation(36, 10), + // (38,40): error CS0539: 'C6.M01' in explicit interface declaration is not found among members of the interface that can be implemented + // static event System.Action I1.M01 { add{} remove{}} + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("C6.M01").WithLocation(38, 40) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticEvent_02(bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@"#pragma warning disable CS0067 // WRN_UnreferencedEvent +public interface I1 +{ + abstract event System.Action M01; +} + +" + typeKeyword + @" + C1 : I1 +{} + +" + typeKeyword + @" + C2 : I1 +{ + public static event System.Action M01; +} + +" + typeKeyword + @" + C3 : I1 +{ + event System.Action M01; +} + +" + typeKeyword + @" + C4 : I1 +{ + static event System.Action I1.M01 { add{} remove{} } +} + +" + typeKeyword + @" + C5 : I1 +{ + public event System.Action M01; +} + +" + typeKeyword + @" + C6 : I1 +{ + event System.Action I1.M01 { add{} remove{} } +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (8,10): error CS0535: 'C1' does not implement interface member 'I1.M01' + // C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01").WithLocation(8, 10), + // (12,10): error CS0736: 'C2' does not implement instance interface member 'I1.M01'. 'C2.M01' cannot implement the interface member because it is static. + // C2 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberStatic, "I1").WithArguments("C2", "I1.M01", "C2.M01").WithLocation(12, 10), + // (18,10): error CS0737: 'C3' does not implement interface member 'I1.M01'. 'C3.M01' cannot implement an interface member because it is not public. + // C3 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotPublic, "I1").WithArguments("C3", "I1.M01", "C3.M01").WithLocation(18, 10), + // (24,10): error CS0535: 'C4' does not implement interface member 'I1.M01' + // C4 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C4", "I1.M01").WithLocation(24, 10), + // (26,35): error CS0539: 'C4.M01' in explicit interface declaration is not found among members of the interface that can be implemented + // static event System.Action I1.M01 { add{} remove{} } + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("C4.M01").WithLocation(26, 35), + // (30,10): error CS0738: 'C5' does not implement interface member 'I1.M01'. 'C5.M01' cannot implement 'I1.M01' because it does not have the matching return type of 'Action'. + // C5 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberWrongReturnType, "I1").WithArguments("C5", "I1.M01", "C5.M01", "System.Action").WithLocation(30, 10), + // (36,10): error CS0535: 'C6' does not implement interface member 'I1.M01' + // C6 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C6", "I1.M01").WithLocation(36, 10), + // (38,33): error CS0539: 'C6.M01' in explicit interface declaration is not found among members of the interface that can be implemented + // event System.Action I1.M01 { add{} remove{} } + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("C6.M01").WithLocation(38, 33) + ); + } + + [Fact] + public void ImplementAbstractStaticEvent_03() + { + var source1 = +@"#pragma warning disable CS0067 // WRN_UnreferencedEvent +public interface I1 +{ + abstract static event System.Action M01; +} + +interface I2 : I1 +{} + +interface I3 : I1 +{ + public virtual event System.Action M01 { add{} remove{} } +} + +interface I4 : I1 +{ + static event System.Action M01; +} + +interface I5 : I1 +{ + event System.Action I1.M01 { add{} remove{} } +} + +interface I6 : I1 +{ + static event System.Action I1.M01 { add{} remove{} } +} + +interface I7 : I1 +{ + abstract static event System.Action M01; +} + +interface I8 : I1 +{ + abstract static event System.Action I1.M01; +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (12,40): warning CS0108: 'I3.M01' hides inherited member 'I1.M01'. Use the new keyword if hiding was intended. + // public virtual event System.Action M01 { add{} remove{} } + Diagnostic(ErrorCode.WRN_NewRequired, "M01").WithArguments("I3.M01", "I1.M01").WithLocation(12, 40), + // (17,32): warning CS0108: 'I4.M01' hides inherited member 'I1.M01'. Use the new keyword if hiding was intended. + // static event System.Action M01; + Diagnostic(ErrorCode.WRN_NewRequired, "M01").WithArguments("I4.M01", "I1.M01").WithLocation(17, 32), + // (22,28): error CS0539: 'I5.M01' in explicit interface declaration is not found among members of the interface that can be implemented + // event System.Action I1.M01 { add{} remove{} } + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("I5.M01").WithLocation(22, 28), + // (27,35): error CS0106: The modifier 'static' is not valid for this item + // static event System.Action I1.M01 { add{} remove{} } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M01").WithArguments("static").WithLocation(27, 35), + // (27,35): error CS0539: 'I6.M01' in explicit interface declaration is not found among members of the interface that can be implemented + // static event System.Action I1.M01 { add{} remove{} } + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("I6.M01").WithLocation(27, 35), + // (32,41): warning CS0108: 'I7.M01' hides inherited member 'I1.M01'. Use the new keyword if hiding was intended. + // abstract static event System.Action M01; + Diagnostic(ErrorCode.WRN_NewRequired, "M01").WithArguments("I7.M01", "I1.M01").WithLocation(32, 41), + // (37,44): error CS0106: The modifier 'static' is not valid for this item + // abstract static event System.Action I1.M01 { add{} remove{} } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "M01").WithArguments("static").WithLocation(37, 44), + // (37,44): error CS0539: 'I8.M01' in explicit interface declaration is not found among members of the interface that can be implemented + // abstract static event System.Action I1.M01; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("I8.M01").WithLocation(37, 44) + ); + + foreach (var m01 in compilation1.GlobalNamespace.GetTypeMember("I1").GetMembers()) + { + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I2").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I3").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I4").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I5").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I6").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I7").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I8").FindImplementationForInterfaceMember(m01)); + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticEvent_04(bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static event System.Action M01; + abstract static event System.Action M02; +} +"; + var source2 = +typeKeyword + @" + Test: I1 +{ + static event System.Action I1.M01 { add{} remove => throw null; } + public static event System.Action M02; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (4,35): error CS8703: The modifier 'static' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // static event System.Action I1.M01 { add{} remove => throw null; } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("static", "9.0", "preview").WithLocation(4, 35), + // (5,39): warning CS0067: The event 'Test.M02' is never used + // public static event System.Action M02; + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "M02").WithArguments("Test.M02").WithLocation(5, 39) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation3.VerifyDiagnostics( + // (4,35): error CS8703: The modifier 'static' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // static event System.Action I1.M01 { add{} remove => throw null; } + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("static", "9.0", "preview").WithLocation(4, 35), + // (5,39): warning CS0067: The event 'Test.M02' is never used + // public static event System.Action M02; + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "M02").WithArguments("Test.M02").WithLocation(5, 39), + // (10,41): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static event System.Action M01; + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M01").WithArguments("abstract", "9.0", "preview").WithLocation(10, 41), + // (11,41): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static event System.Action M02; + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M02").WithArguments("abstract", "9.0", "preview").WithLocation(11, 41) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticEvent_05(bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static event System.Action M01; +} +"; + var source2 = +typeKeyword + @" + Test1: I1 +{ + public static event System.Action M01 { add{} remove{} } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (2,12): error CS9110: 'Test1.M01.remove' cannot implement interface member 'I1.M01.remove' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01.remove", "I1.M01.remove", "Test1").WithLocation(2, 12), + // (2,12): error CS9110: 'Test1.M01.add' cannot implement interface member 'I1.M01.add' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01.add", "I1.M01.add", "Test1").WithLocation(2, 12), + // (2,12): error CS9110: 'Test1.M01' cannot implement interface member 'I1.M01' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01", "I1.M01", "Test1").WithLocation(2, 12) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation3.VerifyDiagnostics( + // (2,12): error CS9110: 'Test1.M01.remove' cannot implement interface member 'I1.M01.remove' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01.remove", "I1.M01.remove", "Test1").WithLocation(2, 12), + // (2,12): error CS9110: 'Test1.M01.add' cannot implement interface member 'I1.M01.add' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01.add", "I1.M01.add", "Test1").WithLocation(2, 12), + // (2,12): error CS9110: 'Test1.M01' cannot implement interface member 'I1.M01' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01", "I1.M01", "Test1").WithLocation(2, 12), + // (9,41): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static event System.Action M01; + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(9, 41) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticEvent_06(bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static event System.Action M01; +} +"; + var source2 = +typeKeyword + @" + Test1: I1 +{ + static event System.Action I1.M01 { add => throw null; remove => throw null; } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (4,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // static event System.Action I1.M01 { add => throw null; remove => throw null; } + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(4, 35) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation3.VerifyDiagnostics( + // (4,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // static event System.Action I1.M01 { add => throw null; remove => throw null; } + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(4, 35), + // (9,41): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static event System.Action M01; + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(9, 41) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticEvent_07(bool structure) + { + // Basic implicit implementation scenario, MethodImpl is emitted + + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static event System.Action M01; +} + +" + typeKeyword + @" + C : I1 +{ + public static event System.Action M01 { add => throw null; remove {} } +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped, + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false)).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var m01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); + var m01Add = m01.AddMethod; + var m01Remove = m01.RemoveMethod; + var c = module.GlobalNamespace.GetTypeMember("C"); + + Assert.Equal(1, c.GetMembers().OfType().Count()); + Assert.Equal(2, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var cM01 = (EventSymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + + Assert.Equal("event System.Action C.M01", cM01.ToTestDisplayString()); + + var cM01Add = cM01.AddMethod; + Assert.Same(cM01Add, c.FindImplementationForInterfaceMember(m01Add)); + + Assert.True(cM01Add.IsStatic); + Assert.False(cM01Add.IsAbstract); + Assert.False(cM01Add.IsVirtual); + Assert.False(cM01Add.IsMetadataVirtual()); + Assert.False(cM01Add.IsMetadataFinal); + Assert.False(cM01Add.IsMetadataNewSlot()); + Assert.Equal(MethodKind.EventAdd, cM01Add.MethodKind); + Assert.False(cM01Add.HasRuntimeSpecialName); + Assert.True(cM01Add.HasSpecialName); + + Assert.Equal("void C.M01.add", cM01Add.ToTestDisplayString()); + + var cM01Remove = cM01.RemoveMethod; + Assert.Same(cM01Remove, c.FindImplementationForInterfaceMember(m01Remove)); + + Assert.True(cM01Remove.IsStatic); + Assert.False(cM01Remove.IsAbstract); + Assert.False(cM01Remove.IsVirtual); + Assert.False(cM01Remove.IsMetadataVirtual()); + Assert.False(cM01Remove.IsMetadataFinal); + Assert.False(cM01Remove.IsMetadataNewSlot()); + Assert.Equal(MethodKind.EventRemove, cM01Remove.MethodKind); + Assert.False(cM01Remove.HasRuntimeSpecialName); + Assert.True(cM01Remove.HasSpecialName); + + Assert.Equal("void C.M01.remove", cM01Remove.ToTestDisplayString()); + + if (module is PEModuleSymbol) + { + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01Add, cM01Add.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01Remove, cM01Remove.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(cM01.ExplicitInterfaceImplementations); + Assert.Empty(cM01Add.ExplicitInterfaceImplementations); + Assert.Empty(cM01Remove.ExplicitInterfaceImplementations); + } + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticEvent_08(bool structure) + { + // Basic explicit implementation scenario + + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 +{ + abstract static event System.Action M01; +} + +" + typeKeyword + @" + C : I1 +{ + static event System.Action I1.M01 { add => throw null; remove {} } +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped, + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false)).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var m01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); + var m01Add = m01.AddMethod; + var m01Remove = m01.RemoveMethod; + var c = module.GlobalNamespace.GetTypeMember("C"); + + Assert.Equal(1, c.GetMembers().OfType().Count()); + Assert.Equal(2, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var cM01 = (EventSymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + + Assert.Equal("event System.Action C.I1.M01", cM01.ToTestDisplayString()); + + var cM01Add = cM01.AddMethod; + Assert.Same(cM01Add, c.FindImplementationForInterfaceMember(m01Add)); + + Assert.True(cM01Add.IsStatic); + Assert.False(cM01Add.IsAbstract); + Assert.False(cM01Add.IsVirtual); + Assert.False(cM01Add.IsMetadataVirtual()); + Assert.False(cM01Add.IsMetadataFinal); + Assert.False(cM01Add.IsMetadataNewSlot()); + Assert.Equal(MethodKind.EventAdd, cM01Add.MethodKind); + Assert.False(cM01Add.HasRuntimeSpecialName); + Assert.True(cM01Add.HasSpecialName); + + Assert.Equal("void C.I1.M01.add", cM01Add.ToTestDisplayString()); + + var cM01Remove = cM01.RemoveMethod; + Assert.Same(cM01Remove, c.FindImplementationForInterfaceMember(m01Remove)); + + Assert.True(cM01Remove.IsStatic); + Assert.False(cM01Remove.IsAbstract); + Assert.False(cM01Remove.IsVirtual); + Assert.False(cM01Remove.IsMetadataVirtual()); + Assert.False(cM01Remove.IsMetadataFinal); + Assert.False(cM01Remove.IsMetadataNewSlot()); + Assert.Equal(MethodKind.EventRemove, cM01Remove.MethodKind); + Assert.False(cM01Remove.HasRuntimeSpecialName); + Assert.True(cM01Remove.HasSpecialName); + + Assert.Equal("void C.I1.M01.remove", cM01Remove.ToTestDisplayString()); + + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01Add, cM01Add.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01Remove, cM01Remove.ExplicitInterfaceImplementations.Single()); + } + } + + [Fact] + public void ImplementAbstractStaticEvent_09() + { + // Explicit implementation from base is treated as an implementation + + var source1 = +@" +public interface I1 +{ + abstract static event System.Action M01; +} + +public class C1 +{ + public static event System.Action M01 { add => throw null; remove {} } +} + +public class C2 : C1, I1 +{ + static event System.Action I1.M01 { add => throw null; remove {} } +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C3 : C2, I1 +{ +} +"; + + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } + + void validate(ModuleSymbol module) + { + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + Assert.Empty(c3.GetMembers().OfType()); + Assert.Empty(c3.GetMembers().OfType().Where(m => !m.IsConstructor())); + var m01 = c3.Interfaces().Single().GetMembers().OfType().Single(); + + var cM01 = (EventSymbol)c3.FindImplementationForInterfaceMember(m01); + + Assert.Equal("event System.Action C2.I1.M01", cM01.ToTestDisplayString()); + + Assert.Same(cM01.AddMethod, c3.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Same(cM01.RemoveMethod, c3.FindImplementationForInterfaceMember(m01.RemoveMethod)); + + Assert.Same(m01, cM01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.AddMethod, cM01.AddMethod.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.RemoveMethod, cM01.RemoveMethod.ExplicitInterfaceImplementations.Single()); + } + } + + [Fact] + public void ImplementAbstractStaticEvent_10() + { + // Implicit implementation is considered only for types implementing interface in source. + // In metadata, only explicit implementations are considered + + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig specialname abstract virtual static + void add_M01 ( + class [mscorlib]System.Action 'value' + ) cil managed + { + } + + .method public hidebysig specialname abstract virtual static + void remove_M01 ( + class [mscorlib]System.Action 'value' + ) cil managed + { + } + + .event [mscorlib]System.Action M01 + { + .addon void I1::add_M01(class [mscorlib]System.Action) + .removeon void I1::remove_M01(class [mscorlib]System.Action) + } +} + +.class public auto ansi beforefieldinit C1 + extends System.Object + implements I1 +{ + .method private hidebysig specialname static + void I1.add_M01 ( + class [mscorlib]System.Action 'value' + ) cil managed + { + .override method void I1::add_M01(class [mscorlib]System.Action) + IL_0000: ret + } + + .method private hidebysig specialname static + void I1.remove_M01 ( + class [mscorlib]System.Action 'value' + ) cil managed + { + .override method void I1::remove_M01(class [mscorlib]System.Action) + IL_0000: ret + } + + .event [mscorlib]System.Action I1.M01 + { + .addon void C1::I1.add_M01(class [mscorlib]System.Action) + .removeon void C1::I1.remove_M01(class [mscorlib]System.Action) + } + + .method public hidebysig specialname static + void add_M01 ( + class [mscorlib]System.Action 'value' + ) cil managed + { + IL_0000: ret + } + + .method public hidebysig specialname static + void remove_M01 ( + class [mscorlib]System.Action 'value' + ) cil managed + { + IL_0000: ret + } + + .event [mscorlib]System.Action M01 + { + .addon void C1::add_M01(class [mscorlib]System.Action) + .removeon void C1::remove_M01(class [mscorlib]System.Action) + } + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + IL_0000: ldarg.0 + IL_0001: call instance void System.Object::.ctor() + IL_0006: ret + } +} + +.class public auto ansi beforefieldinit C2 + extends C1 + implements I1 +{ + .method public hidebysig specialname static + void add_M01 ( + class [mscorlib]System.Action 'value' + ) cil managed + { + IL_0000: ret + } + + .method public hidebysig specialname static + void remove_M01 ( + class [mscorlib]System.Action 'value' + ) cil managed + { + IL_0000: ret + } + + .event [mscorlib]System.Action M01 + { + .addon void C2::add_M01(class [mscorlib]System.Action) + .removeon void C2::remove_M01(class [mscorlib]System.Action) + } + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + IL_0000: ldarg.0 + IL_0001: call instance void C1::.ctor() + IL_0006: ret + } +} +"; + var source1 = +@" +public class C3 : C2 +{ +} + +public class C4 : C1, I1 +{ +} + +public class C5 : C2, I1 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); + + var c1M01 = (EventSymbol)c1.FindImplementationForInterfaceMember(m01); + + Assert.Equal("event System.Action C1.I1.M01", c1M01.ToTestDisplayString()); + + Assert.Same(c1M01.AddMethod, c1.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Same(c1M01.RemoveMethod, c1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.AddMethod, c1M01.AddMethod.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.RemoveMethod, c1M01.RemoveMethod.ExplicitInterfaceImplementations.Single()); + + var c2 = compilation1.GlobalNamespace.GetTypeMember("C2"); + Assert.Same(c1M01, c2.FindImplementationForInterfaceMember(m01)); + Assert.Same(c1M01.AddMethod, c2.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Same(c1M01.RemoveMethod, c2.FindImplementationForInterfaceMember(m01.RemoveMethod)); + + var c3 = compilation1.GlobalNamespace.GetTypeMember("C3"); + Assert.Same(c1M01, c3.FindImplementationForInterfaceMember(m01)); + Assert.Same(c1M01.AddMethod, c3.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Same(c1M01.RemoveMethod, c3.FindImplementationForInterfaceMember(m01.RemoveMethod)); + + var c4 = compilation1.GlobalNamespace.GetTypeMember("C4"); + Assert.Same(c1M01, c4.FindImplementationForInterfaceMember(m01)); + Assert.Same(c1M01.AddMethod, c4.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Same(c1M01.RemoveMethod, c4.FindImplementationForInterfaceMember(m01.RemoveMethod)); + + var c5 = compilation1.GlobalNamespace.GetTypeMember("C5"); + + var c2M01 = (EventSymbol)c5.FindImplementationForInterfaceMember(m01); + Assert.Equal("event System.Action C2.M01", c2M01.ToTestDisplayString()); + Assert.Same(c2M01.AddMethod, c5.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Same(c2M01.RemoveMethod, c5.FindImplementationForInterfaceMember(m01.RemoveMethod)); + + compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + } + + [Fact] + public void ImplementAbstractStaticEvent_11() + { + // Ignore invalid metadata (non-abstract static virtual method). + scenario1(); + scenario2(); + scenario3(); + + void scenario1() + { + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig specialname static virtual + void add_M01 ( + class [mscorlib]System.Action 'value' + ) cil managed + { + IL_0000: ret + } + + .method public hidebysig specialname static virtual + void remove_M01 ( + class [mscorlib]System.Action 'value' + ) cil managed + { + IL_0000: ret + } + + .event [mscorlib]System.Action M01 + { + .addon void I1::add_M01(class [mscorlib]System.Action) + .removeon void I1::remove_M01(class [mscorlib]System.Action) + } +} +"; + + var source1 = +@" +public class C1 : I1 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyEmitDiagnostics(); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var i1 = c1.Interfaces().Single(); + var m01 = i1.GetMembers().OfType().Single(); + + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + + compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyEmitDiagnostics(); + + var source2 = +@" +public class C1 : I1 +{ + static event System.Action I1.M01 { add{} remove{} } +} +"; + + var compilation2 = CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation2.VerifyEmitDiagnostics( + // (4,34): error CS0539: 'C1.M01' in explicit interface declaration is not found among members of the interface that can be implemented + // static event System.Action I1.M01 { add{} remove{} } + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M01").WithArguments("C1.M01").WithLocation(4, 34) + ); + + c1 = compilation2.GlobalNamespace.GetTypeMember("C1"); + m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); + + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + + var source3 = +@" +public class C1 : I1 +{ + public static event System.Action M01 { add{} remove{} } +} +"; + + var compilation3 = CreateCompilationWithIL(source3, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation3, sourceSymbolValidator: validate3, symbolValidator: validate3, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate3(ModuleSymbol module) + { + var c = module.GlobalNamespace.GetTypeMember("C1"); + Assert.Equal(1, c.GetMembers().OfType().Count()); + Assert.Equal(2, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var m01 = c.Interfaces().Single().GetMembers().OfType().Single(); + Assert.Null(c.FindImplementationForInterfaceMember(m01)); + Assert.Null(c.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Null(c.FindImplementationForInterfaceMember(m01.RemoveMethod)); + } + } + + void scenario2() + { + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig specialname abstract virtual static + void add_M01 ( + class [mscorlib]System.Action 'value' + ) cil managed + { + } + + .method public hidebysig specialname static virtual + void remove_M01 ( + class [mscorlib]System.Action 'value' + ) cil managed + { + IL_0000: ret + } + + .event [mscorlib]System.Action M01 + { + .addon void I1::add_M01(class [mscorlib]System.Action) + .removeon void I1::remove_M01(class [mscorlib]System.Action) + } +} +"; + + var source1 = +@" +public class C1 : I1 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (2,19): error CS0535: 'C1' does not implement interface member 'I1.M01' + // public class C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01").WithLocation(2, 19) + ); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var i1 = c1.Interfaces().Single(); + var m01 = i1.GetMembers().OfType().Single(); + + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + + var source2 = +@" +public class C1 : I1 +{ + static event System.Action I1.M01 { add {} } +} +"; + + var compilation2 = CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation2.VerifyDiagnostics( + // (4,34): error CS0065: 'C1.I1.M01': event property must have both add and remove accessors + // static event System.Action I1.M01 { add {} } + Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "M01").WithArguments("C1.I1.M01").WithLocation(4, 34) + ); + + c1 = compilation2.GlobalNamespace.GetTypeMember("C1"); + i1 = c1.Interfaces().Single(); + m01 = i1.GetMembers().OfType().Single(); + var c1M01 = c1.GetMembers().OfType().Single(); + + Assert.Same(c1M01, c1.FindImplementationForInterfaceMember(m01)); + Assert.Same(c1M01.AddMethod, c1.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.AddMethod, c1M01.AddMethod.ExplicitInterfaceImplementations.Single()); + + var source3 = +@" +public class C1 : I1 +{ + public static event System.Action M01 { add{} remove{} } +} +"; + + var compilation3 = CreateCompilationWithIL(source3, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation3, sourceSymbolValidator: validate3, symbolValidator: validate3, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate3(ModuleSymbol module) + { + var c = module.GlobalNamespace.GetTypeMember("C1"); + + var m01 = c.Interfaces().Single().GetMembers().OfType().Single(); + var m01Add = m01.AddMethod; + + Assert.Equal(1, c.GetMembers().OfType().Count()); + Assert.Equal(2, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var cM01 = (EventSymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + + Assert.Equal("event System.Action C1.M01", cM01.ToTestDisplayString()); + + var cM01Add = cM01.AddMethod; + Assert.Same(cM01Add, c.FindImplementationForInterfaceMember(m01Add)); + + Assert.True(cM01Add.IsStatic); + Assert.False(cM01Add.IsAbstract); + Assert.False(cM01Add.IsVirtual); + Assert.False(cM01Add.IsMetadataVirtual()); + Assert.False(cM01Add.IsMetadataFinal); + Assert.False(cM01Add.IsMetadataNewSlot()); + Assert.Equal(MethodKind.EventAdd, cM01Add.MethodKind); + + Assert.Equal("void C1.M01.add", cM01Add.ToTestDisplayString()); + + var cM01Remove = cM01.RemoveMethod; + + Assert.True(cM01Remove.IsStatic); + Assert.False(cM01Remove.IsAbstract); + Assert.False(cM01Remove.IsVirtual); + Assert.False(cM01Remove.IsMetadataVirtual()); + Assert.False(cM01Remove.IsMetadataFinal); + Assert.False(cM01Remove.IsMetadataNewSlot()); + Assert.Equal(MethodKind.EventRemove, cM01Remove.MethodKind); + + Assert.Equal("void C1.M01.remove", cM01Remove.ToTestDisplayString()); + + Assert.Null(c.FindImplementationForInterfaceMember(m01.RemoveMethod)); + + if (module is PEModuleSymbol) + { + Assert.Same(m01Add, cM01Add.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(cM01Add.ExplicitInterfaceImplementations); + } + + Assert.Empty(cM01.ExplicitInterfaceImplementations); + Assert.Empty(cM01Remove.ExplicitInterfaceImplementations); + } + + var source4 = +@" +public class C1 : I1 +{ + static event System.Action I1.M01 { add{} remove{} } +} +"; + + var compilation4 = CreateCompilationWithIL(source4, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation4.VerifyDiagnostics( + // (4,46): error CS0550: 'C1.I1.M01.remove' adds an accessor not found in interface member 'I1.M01' + // static event System.Action I1.M01 { add{} remove{} } + Diagnostic(ErrorCode.ERR_ExplicitPropertyAddingAccessor, "remove").WithArguments("C1.I1.M01.remove", "I1.M01").WithLocation(4, 46) + ); + + c1 = compilation4.GlobalNamespace.GetTypeMember("C1"); + i1 = c1.Interfaces().Single(); + m01 = i1.GetMembers().OfType().Single(); + c1M01 = c1.GetMembers().OfType().Single(); + + Assert.Same(c1M01, c1.FindImplementationForInterfaceMember(m01)); + Assert.Same(c1M01.AddMethod, c1.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.AddMethod, c1M01.AddMethod.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.RemoveMethod, c1M01.RemoveMethod.ExplicitInterfaceImplementations.Single()); + + var source5 = +@" +public class C1 : I1 +{ + public static event System.Action M01 { add{} } +} +"; + + var compilation5 = CreateCompilationWithIL(source5, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation5.VerifyDiagnostics( + // (4,38): error CS0065: 'C1.M01': event property must have both add and remove accessors + // public static event System.Action M01 { add{} } + Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "M01").WithArguments("C1.M01").WithLocation(4, 38) + ); + + c1 = compilation5.GlobalNamespace.GetTypeMember("C1"); + i1 = c1.Interfaces().Single(); + m01 = i1.GetMembers().OfType().Single(); + c1M01 = c1.GetMembers().OfType().Single(); + + Assert.Same(c1M01, c1.FindImplementationForInterfaceMember(m01)); + Assert.Same(c1M01.AddMethod, c1.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + + var source6 = +@" +public class C1 : I1 +{ + public static event System.Action M01 { remove{} } +} +"; + + var compilation6 = CreateCompilationWithIL(source6, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation6.VerifyDiagnostics( + // (2,19): error CS0535: 'C1' does not implement interface member 'I1.M01.add' + // public class C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01.add").WithLocation(2, 19), + // (4,38): error CS0065: 'C1.M01': event property must have both add and remove accessors + // public static event System.Action M01 { remove{} } + Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "M01").WithArguments("C1.M01").WithLocation(4, 38) + ); + + c1 = compilation6.GlobalNamespace.GetTypeMember("C1"); + i1 = c1.Interfaces().Single(); + m01 = i1.GetMembers().OfType().Single(); + c1M01 = c1.GetMembers().OfType().Single(); + + Assert.Same(c1M01, c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + + var source7 = +@" +public class C1 : I1 +{ + static event System.Action I1.M01 { remove{} } +} +"; + + var compilation7 = CreateCompilationWithIL(source7, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation7.VerifyDiagnostics( + // (2,19): error CS0535: 'C1' does not implement interface member 'I1.M01.add' + // public class C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01.add").WithLocation(2, 19), + // (4,34): error CS0065: 'C1.I1.M01': event property must have both add and remove accessors + // static event System.Action I1.M01 { remove{} } + Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "M01").WithArguments("C1.I1.M01").WithLocation(4, 34), + // (4,40): error CS0550: 'C1.I1.M01.remove' adds an accessor not found in interface member 'I1.M01' + // static event System.Action I1.M01 { remove{} } + Diagnostic(ErrorCode.ERR_ExplicitPropertyAddingAccessor, "remove").WithArguments("C1.I1.M01.remove", "I1.M01").WithLocation(4, 40) + ); + + c1 = compilation7.GlobalNamespace.GetTypeMember("C1"); + i1 = c1.Interfaces().Single(); + m01 = i1.GetMembers().OfType().Single(); + c1M01 = c1.GetMembers().OfType().Single(); + + Assert.Same(c1M01, c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.RemoveMethod, c1M01.RemoveMethod.ExplicitInterfaceImplementations.Single()); + } + + void scenario3() + { + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig specialname static virtual + void add_M01 ( + class [mscorlib]System.Action 'value' + ) cil managed + { + IL_0000: ret + } + + .method public hidebysig specialname abstract virtual static + void remove_M01 ( + class [mscorlib]System.Action 'value' + ) cil managed + { + } + + .event [mscorlib]System.Action M01 + { + .addon void I1::add_M01(class [mscorlib]System.Action) + .removeon void I1::remove_M01(class [mscorlib]System.Action) + } +} +"; + + var source1 = +@" +public class C1 : I1 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (2,19): error CS0535: 'C1' does not implement interface member 'I1.M01' + // public class C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01").WithLocation(2, 19) + ); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var i1 = c1.Interfaces().Single(); + var m01 = i1.GetMembers().OfType().Single(); + + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01.AddMethod)); + + var source2 = +@" +public class C1 : I1 +{ + static event System.Action I1.M01 { remove {} } +} +"; + + var compilation2 = CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation2.VerifyDiagnostics( + // (4,34): error CS0065: 'C1.I1.M01': event property must have both add and remove accessors + // static event System.Action I1.M01 { add {} } + Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "M01").WithArguments("C1.I1.M01").WithLocation(4, 34) + ); + + c1 = compilation2.GlobalNamespace.GetTypeMember("C1"); + i1 = c1.Interfaces().Single(); + m01 = i1.GetMembers().OfType().Single(); + var c1M01 = c1.GetMembers().OfType().Single(); + + Assert.Same(c1M01, c1.FindImplementationForInterfaceMember(m01)); + Assert.Same(c1M01.RemoveMethod, c1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.RemoveMethod, c1M01.RemoveMethod.ExplicitInterfaceImplementations.Single()); + + var source3 = +@" +public class C1 : I1 +{ + public static event System.Action M01 { add{} remove{} } +} +"; + + var compilation3 = CreateCompilationWithIL(source3, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation3, sourceSymbolValidator: validate3, symbolValidator: validate3, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate3(ModuleSymbol module) + { + var c = module.GlobalNamespace.GetTypeMember("C1"); + + var m01 = c.Interfaces().Single().GetMembers().OfType().Single(); + var m01Remove = m01.RemoveMethod; + + Assert.Equal(1, c.GetMembers().OfType().Count()); + Assert.Equal(2, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var cM01 = (EventSymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + + Assert.Equal("event System.Action C1.M01", cM01.ToTestDisplayString()); + + var cM01Remove = cM01.RemoveMethod; + Assert.Same(cM01Remove, c.FindImplementationForInterfaceMember(m01Remove)); + + Assert.True(cM01Remove.IsStatic); + Assert.False(cM01Remove.IsAbstract); + Assert.False(cM01Remove.IsVirtual); + Assert.False(cM01Remove.IsMetadataVirtual()); + Assert.False(cM01Remove.IsMetadataFinal); + Assert.False(cM01Remove.IsMetadataNewSlot()); + Assert.Equal(MethodKind.EventRemove, cM01Remove.MethodKind); + + Assert.Equal("void C1.M01.remove", cM01Remove.ToTestDisplayString()); + + var cM01Add = cM01.AddMethod; + + Assert.True(cM01Add.IsStatic); + Assert.False(cM01Add.IsAbstract); + Assert.False(cM01Add.IsVirtual); + Assert.False(cM01Add.IsMetadataVirtual()); + Assert.False(cM01Add.IsMetadataFinal); + Assert.False(cM01Add.IsMetadataNewSlot()); + Assert.Equal(MethodKind.EventAdd, cM01Add.MethodKind); + + Assert.Equal("void C1.M01.add", cM01Add.ToTestDisplayString()); + + Assert.Null(c.FindImplementationForInterfaceMember(m01.AddMethod)); + + if (module is PEModuleSymbol) + { + Assert.Same(m01Remove, cM01Remove.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(cM01Remove.ExplicitInterfaceImplementations); + } + + Assert.Empty(cM01.ExplicitInterfaceImplementations); + Assert.Empty(cM01Add.ExplicitInterfaceImplementations); + } + + var source4 = +@" +public class C1 : I1 +{ + static event System.Action I1.M01 { add{} remove{} } +} +"; + + var compilation4 = CreateCompilationWithIL(source4, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation4.VerifyDiagnostics( + // (4,40): error CS0550: 'C1.I1.M01.add' adds an accessor not found in interface member 'I1.M01' + // static event System.Action I1.M01 { add{} remove{} } + Diagnostic(ErrorCode.ERR_ExplicitPropertyAddingAccessor, "add").WithArguments("C1.I1.M01.add", "I1.M01").WithLocation(4, 40) + ); + + c1 = compilation4.GlobalNamespace.GetTypeMember("C1"); + i1 = c1.Interfaces().Single(); + m01 = i1.GetMembers().OfType().Single(); + c1M01 = c1.GetMembers().OfType().Single(); + + Assert.Same(c1M01, c1.FindImplementationForInterfaceMember(m01)); + Assert.Same(c1M01.RemoveMethod, c1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.RemoveMethod, c1M01.RemoveMethod.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.AddMethod, c1M01.AddMethod.ExplicitInterfaceImplementations.Single()); + + var source5 = +@" +public class C1 : I1 +{ + public static event System.Action M01 { remove{} } +} +"; + + var compilation5 = CreateCompilationWithIL(source5, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation5.VerifyDiagnostics( + // (4,38): error CS0065: 'C1.M01': event property must have both add and remove accessors + // public static event System.Action M01 { remove{} } + Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "M01").WithArguments("C1.M01").WithLocation(4, 38) + ); + + c1 = compilation5.GlobalNamespace.GetTypeMember("C1"); + i1 = c1.Interfaces().Single(); + m01 = i1.GetMembers().OfType().Single(); + c1M01 = c1.GetMembers().OfType().Single(); + + Assert.Same(c1M01, c1.FindImplementationForInterfaceMember(m01)); + Assert.Same(c1M01.RemoveMethod, c1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.AddMethod)); + + var source6 = +@" +public class C1 : I1 +{ + public static event System.Action M01 { add{} } +} +"; + + var compilation6 = CreateCompilationWithIL(source6, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation6.VerifyDiagnostics( + // (2,19): error CS0535: 'C1' does not implement interface member 'I1.M01.remove' + // public class C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01.remove").WithLocation(2, 19), + // (4,38): error CS0065: 'C1.M01': event property must have both add and remove accessors + // public static event System.Action M01 { remove{} } + Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "M01").WithArguments("C1.M01").WithLocation(4, 38) + ); + + c1 = compilation6.GlobalNamespace.GetTypeMember("C1"); + i1 = c1.Interfaces().Single(); + m01 = i1.GetMembers().OfType().Single(); + c1M01 = c1.GetMembers().OfType().Single(); + + Assert.Same(c1M01, c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + + var source7 = +@" +public class C1 : I1 +{ + static event System.Action I1.M01 { add{} } +} +"; + + var compilation7 = CreateCompilationWithIL(source7, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation7.VerifyDiagnostics( + // (2,19): error CS0535: 'C1' does not implement interface member 'I1.M01.remove' + // public class C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01.remove").WithLocation(2, 19), + // (4,34): error CS0065: 'C1.I1.M01': event property must have both add and remove accessors + // static event System.Action I1.M01 { add{} } + Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "M01").WithArguments("C1.I1.M01").WithLocation(4, 34), + // (4,40): error CS0550: 'C1.I1.M01.add' adds an accessor not found in interface member 'I1.M01' + // static event System.Action I1.M01 { add{} } + Diagnostic(ErrorCode.ERR_ExplicitPropertyAddingAccessor, "add").WithArguments("C1.I1.M01.add", "I1.M01").WithLocation(4, 40) + ); + + c1 = compilation7.GlobalNamespace.GetTypeMember("C1"); + i1 = c1.Interfaces().Single(); + m01 = i1.GetMembers().OfType().Single(); + c1M01 = c1.GetMembers().OfType().Single(); + + Assert.Same(c1M01, c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.AddMethod, c1M01.AddMethod.ExplicitInterfaceImplementations.Single()); + } + } + + [Fact] + public void ImplementAbstractStaticEvent_12() + { + // Ignore invalid metadata (default interface implementation for a static method) + + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig specialname abstract virtual static + void add_M01 ( + class [mscorlib]System.Action 'value' + ) cil managed + { + } + + .method public hidebysig specialname abstract virtual static + void remove_M01 ( + class [mscorlib]System.Action 'value' + ) cil managed + { + } + + .event [mscorlib]System.Action M01 + { + .addon void I1::add_M01(class [mscorlib]System.Action) + .removeon void I1::remove_M01(class [mscorlib]System.Action) + } +} + +.class interface public auto ansi abstract I2 + implements I1 +{ + .method private hidebysig specialname static + void I1.add_M01 ( + class [mscorlib]System.Action 'value' + ) cil managed + { + .override method void I1::add_M01(class [mscorlib]System.Action) + IL_0000: ret + } + + .method private hidebysig specialname static + void I1.remove_M01 ( + class [mscorlib]System.Action 'value' + ) cil managed + { + .override method void I1::remove_M01(class [mscorlib]System.Action) + IL_0000: ret + } + + .event [mscorlib]System.Action I1.M01 + { + .addon void I2::I1.add_M01(class [mscorlib]System.Action) + .removeon void I2::I1.remove_M01(class [mscorlib]System.Action) + } +} +"; + + var source1 = +@" +public class C1 : I2 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyEmitDiagnostics( + // (2,19): error CS0535: 'C1' does not implement interface member 'I1.M01' + // public class C1 : I2 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I2").WithArguments("C1", "I1.M01").WithLocation(2, 19) + ); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var i2 = c1.Interfaces().Single(); + var i1 = i2.Interfaces().Single(); + var m01 = i1.GetMembers().OfType().Single(); + + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(i2.FindImplementationForInterfaceMember(m01)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Null(i2.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Null(c1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + Assert.Null(i2.FindImplementationForInterfaceMember(m01.RemoveMethod)); + + var i2M01 = i2.GetMembers().OfType().Single(); + Assert.Same(m01, i2M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.AddMethod, i2M01.AddMethod.ExplicitInterfaceImplementations.Single()); + Assert.Same(m01.RemoveMethod, i2M01.RemoveMethod.ExplicitInterfaceImplementations.Single()); + } + + [Fact] + public void ImplementAbstractStaticEvent_13() + { + // A forwarding method is added for an implicit implementation declared in base class. + + var source1 = +@" +public interface I1 +{ + abstract static event System.Action M01; +} + +class C1 +{ + public static event System.Action M01 { add => throw null; remove{} } +} + +class C2 : C1, I1 +{ +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var m01 = module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + + var c2M01 = (EventSymbol)c2.FindImplementationForInterfaceMember(m01); + var c2M01Add = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01.AddMethod); + var c2M01Remove = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01.RemoveMethod); + + Assert.True(c2M01Add.IsStatic); + Assert.False(c2M01Add.IsAbstract); + Assert.False(c2M01Add.IsVirtual); + Assert.False(c2M01Add.IsMetadataVirtual()); + Assert.False(c2M01Add.IsMetadataFinal); + Assert.False(c2M01Add.IsMetadataNewSlot()); + + Assert.True(c2M01Remove.IsStatic); + Assert.False(c2M01Remove.IsAbstract); + Assert.False(c2M01Remove.IsVirtual); + Assert.False(c2M01Remove.IsMetadataVirtual()); + Assert.False(c2M01Remove.IsMetadataFinal); + Assert.False(c2M01Remove.IsMetadataNewSlot()); + + if (module is PEModuleSymbol) + { + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c2M01Add.MethodKind); + Assert.False(c2M01Add.HasRuntimeSpecialName); + Assert.False(c2M01Add.HasSpecialName); + Assert.Equal("void C2.I1.add_M01(System.Action value)", c2M01Add.ToTestDisplayString()); + Assert.Same(m01.AddMethod, c2M01Add.ExplicitInterfaceImplementations.Single()); + + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c2M01Remove.MethodKind); + Assert.False(c2M01Remove.HasRuntimeSpecialName); + Assert.False(c2M01Remove.HasSpecialName); + Assert.Equal("void C2.I1.remove_M01(System.Action value)", c2M01Remove.ToTestDisplayString()); + Assert.Same(m01.RemoveMethod, c2M01Remove.ExplicitInterfaceImplementations.Single()); + + // Forwarding methods for accessors aren't tied to a property + Assert.Null(c2M01); + + var c1M01 = module.GlobalNamespace.GetMember("C1.M01"); + var c1M01Add = c1M01.AddMethod; + var c1M01Remove = c1M01.RemoveMethod; + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + + Assert.True(c1M01Add.IsStatic); + Assert.False(c1M01Add.IsAbstract); + Assert.False(c1M01Add.IsVirtual); + Assert.False(c1M01Add.IsMetadataVirtual()); + Assert.False(c1M01Add.IsMetadataFinal); + Assert.False(c1M01Add.IsMetadataNewSlot()); + Assert.Equal(MethodKind.EventAdd, c1M01Add.MethodKind); + Assert.False(c1M01Add.HasRuntimeSpecialName); + Assert.True(c1M01Add.HasSpecialName); + Assert.Empty(c1M01Add.ExplicitInterfaceImplementations); + + Assert.True(c1M01Remove.IsStatic); + Assert.False(c1M01Remove.IsAbstract); + Assert.False(c1M01Remove.IsVirtual); + Assert.False(c1M01Remove.IsMetadataVirtual()); + Assert.False(c1M01Remove.IsMetadataFinal); + Assert.False(c1M01Remove.IsMetadataNewSlot()); + Assert.Equal(MethodKind.EventRemove, c1M01Remove.MethodKind); + Assert.False(c1M01Remove.HasRuntimeSpecialName); + Assert.True(c1M01Remove.HasSpecialName); + Assert.Empty(c1M01Remove.ExplicitInterfaceImplementations); + } + else + { + Assert.True(c2M01.IsStatic); + Assert.False(c2M01.IsAbstract); + Assert.False(c2M01.IsVirtual); + + Assert.Equal("event System.Action C1.M01", c2M01.ToTestDisplayString()); + Assert.Empty(c2M01.ExplicitInterfaceImplementations); + + Assert.Equal(MethodKind.EventAdd, c2M01Add.MethodKind); + Assert.False(c2M01Add.HasRuntimeSpecialName); + Assert.True(c2M01Add.HasSpecialName); + Assert.Same(c2M01.AddMethod, c2M01Add); + Assert.Empty(c2M01Add.ExplicitInterfaceImplementations); + + Assert.Equal(MethodKind.EventRemove, c2M01Remove.MethodKind); + Assert.False(c2M01Remove.HasRuntimeSpecialName); + Assert.True(c2M01Remove.HasSpecialName); + Assert.Same(c2M01.RemoveMethod, c2M01Remove); + Assert.Empty(c2M01Remove.ExplicitInterfaceImplementations); + } + } + + verifier.VerifyIL("C2.I1.add_M01", +@" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""void C1.M01.add"" + IL_0006: ret +} +"); + + verifier.VerifyIL("C2.I1.remove_M01", +@" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""void C1.M01.remove"" + IL_0006: ret +} +"); + } + + [Fact] + public void ImplementAbstractStaticEvent_14() + { + // A forwarding method is added for an implicit implementation with modopt mismatch. + + var ilSource = @" +.class interface public auto ansi abstract I1 +{ + .method public hidebysig specialname abstract virtual static + void add_M01 ( + class [mscorlib]System.Action`1 'value' + ) cil managed + { + } + + .method public hidebysig specialname abstract virtual static + void remove_M01 ( + class [mscorlib]System.Action`1 'value' + ) cil managed + { + } + + .event class [mscorlib]System.Action`1 M01 + { + .addon void I1::add_M01(class [mscorlib]System.Action`1) + .removeon void I1::remove_M01(class [mscorlib]System.Action`1) + } +} +"; + + var source1 = +@" +class C1 : I1 +{ + public static event System.Action M01 { add => throw null; remove{} } +} + +class C2 : I1 +{ + static event System.Action I1.M01 { add => throw null; remove{} } +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c1 = module.GlobalNamespace.GetTypeMember("C1"); + var m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); + + var c1M01 = c1.GetMembers().OfType().Single(); + var c1M01Add = c1M01.AddMethod; + var c1M01Remove = c1M01.RemoveMethod; + + Assert.Equal("event System.Action C1.M01", c1M01.ToTestDisplayString()); + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + + Assert.Equal(MethodKind.EventAdd, c1M01Add.MethodKind); + Assert.Equal("void C1.M01.add", c1M01Add.ToTestDisplayString()); + Assert.Equal("System.Action value", c1M01Add.Parameters.Single().ToTestDisplayString()); + Assert.Empty(c1M01Add.ExplicitInterfaceImplementations); + Assert.True(c1M01Add.IsStatic); + Assert.False(c1M01Add.IsAbstract); + Assert.False(c1M01Add.IsVirtual); + Assert.False(c1M01Add.IsMetadataVirtual()); + Assert.False(c1M01Add.IsMetadataFinal); + Assert.False(c1M01Add.IsMetadataNewSlot()); + + Assert.Equal(MethodKind.EventRemove, c1M01Remove.MethodKind); + Assert.Equal("void C1.M01.remove", c1M01Remove.ToTestDisplayString()); + Assert.Equal("System.Action value", c1M01Remove.Parameters.Single().ToTestDisplayString()); + Assert.Empty(c1M01Remove.ExplicitInterfaceImplementations); + Assert.True(c1M01Remove.IsStatic); + Assert.False(c1M01Remove.IsAbstract); + Assert.False(c1M01Remove.IsVirtual); + Assert.False(c1M01Remove.IsMetadataVirtual()); + Assert.False(c1M01Remove.IsMetadataFinal); + Assert.False(c1M01Remove.IsMetadataNewSlot()); + + if (module is PEModuleSymbol) + { + c1M01Add = (MethodSymbol)c1.FindImplementationForInterfaceMember(m01.AddMethod); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c1M01Add.MethodKind); + Assert.Equal("void C1.I1.add_M01(System.Action value)", c1M01Add.ToTestDisplayString()); + Assert.Same(m01.AddMethod, c1M01Add.ExplicitInterfaceImplementations.Single()); + + Assert.True(c1M01Add.IsStatic); + Assert.False(c1M01Add.IsAbstract); + Assert.False(c1M01Add.IsVirtual); + Assert.False(c1M01Add.IsMetadataVirtual()); + Assert.False(c1M01Add.IsMetadataFinal); + Assert.False(c1M01Add.IsMetadataNewSlot()); + + c1M01Remove = (MethodSymbol)c1.FindImplementationForInterfaceMember(m01.RemoveMethod); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c1M01Remove.MethodKind); + Assert.Equal("void C1.I1.remove_M01(System.Action value)", c1M01Remove.ToTestDisplayString()); + Assert.Same(m01.RemoveMethod, c1M01Remove.ExplicitInterfaceImplementations.Single()); + + Assert.True(c1M01Remove.IsStatic); + Assert.False(c1M01Remove.IsAbstract); + Assert.False(c1M01Remove.IsVirtual); + Assert.False(c1M01Remove.IsMetadataVirtual()); + Assert.False(c1M01Remove.IsMetadataFinal); + Assert.False(c1M01Remove.IsMetadataNewSlot()); + + // Forwarding methods aren't tied to an event + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + } + else + { + Assert.Same(c1M01, c1.FindImplementationForInterfaceMember(m01)); + Assert.Same(c1M01Add, c1.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Same(c1M01Remove, c1.FindImplementationForInterfaceMember(m01.RemoveMethod)); + } + + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + + var c2M01 = (EventSymbol)c2.FindImplementationForInterfaceMember(m01); + var c2M01Add = c2M01.AddMethod; + var c2M01Remove = c2M01.RemoveMethod; + + Assert.Equal("event System.Action C2.I1.M01", c2M01.ToTestDisplayString()); + + Assert.True(c2M01.IsStatic); + Assert.False(c2M01.IsAbstract); + Assert.False(c2M01.IsVirtual); + Assert.Same(m01, c2M01.ExplicitInterfaceImplementations.Single()); + + Assert.True(c2M01Add.IsStatic); + Assert.False(c2M01Add.IsAbstract); + Assert.False(c2M01Add.IsVirtual); + Assert.False(c2M01Add.IsMetadataVirtual()); + Assert.False(c2M01Add.IsMetadataFinal); + Assert.False(c2M01Add.IsMetadataNewSlot()); + Assert.Equal(MethodKind.EventAdd, c2M01Add.MethodKind); + Assert.Equal("void C2.I1.M01.add", c2M01Add.ToTestDisplayString()); + Assert.Equal("System.Action value", c2M01Add.Parameters.Single().ToTestDisplayString()); + Assert.Same(m01.AddMethod, c2M01Add.ExplicitInterfaceImplementations.Single()); + Assert.Same(c2M01Add, c2.FindImplementationForInterfaceMember(m01.AddMethod)); + + Assert.True(c2M01Remove.IsStatic); + Assert.False(c2M01Remove.IsAbstract); + Assert.False(c2M01Remove.IsVirtual); + Assert.False(c2M01Remove.IsMetadataVirtual()); + Assert.False(c2M01Remove.IsMetadataFinal); + Assert.False(c2M01Remove.IsMetadataNewSlot()); + Assert.Equal(MethodKind.EventRemove, c2M01Remove.MethodKind); + Assert.Equal("void C2.I1.M01.remove", c2M01Remove.ToTestDisplayString()); + Assert.Equal("System.Action value", c2M01Remove.Parameters.Single().ToTestDisplayString()); + Assert.Same(m01.RemoveMethod, c2M01Remove.ExplicitInterfaceImplementations.Single()); + Assert.Same(c2M01Remove, c2.FindImplementationForInterfaceMember(m01.RemoveMethod)); + + Assert.Same(c2M01, c2.GetMembers().OfType().Single()); + Assert.Equal(2, c2.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + } + + verifier.VerifyIL("C1.I1.add_M01", +@" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""void C1.M01.add"" + IL_0006: ret +} +"); + + verifier.VerifyIL("C1.I1.remove_M01", +@" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""void C1.M01.remove"" + IL_0006: ret +} +"); + } + + [Fact] + public void ImplementAbstractStaticEvent_15() + { + // A forwarding method isn't created if base class implements interface exactly the same way. + + var source1 = +@" +public interface I1 +{ + abstract static event System.Action M01; + abstract static event System.Action M02; +} + +public class C1 +{ + public static event System.Action M01 { add => throw null; remove{} } +} + +public class C2 : C1, I1 +{ + static event System.Action I1.M02 { add => throw null; remove{} } +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C3 : C2, I1 +{ +} +"; + + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.RegularPreview, TestOptions.Regular9 }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } + + void validate(ModuleSymbol module) + { + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + Assert.Empty(c3.GetMembers().OfType()); + Assert.Empty(c3.GetMembers().OfType().Where(m => !m.IsConstructor())); + + var m01 = c3.Interfaces().Single().GetMembers("M01").OfType().Single(); + + var c1M01 = c3.BaseType().BaseType().GetMember("M01"); + Assert.Equal("event System.Action C1.M01", c1M01.ToTestDisplayString()); + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + + var c1M01Add = c1M01.AddMethod; + Assert.True(c1M01Add.IsStatic); + Assert.False(c1M01Add.IsAbstract); + Assert.False(c1M01Add.IsVirtual); + Assert.False(c1M01Add.IsMetadataVirtual()); + Assert.False(c1M01Add.IsMetadataFinal); + Assert.False(c1M01Add.IsMetadataNewSlot()); + + Assert.Empty(c1M01Add.ExplicitInterfaceImplementations); + + var c1M01Remove = c1M01.RemoveMethod; + Assert.True(c1M01Remove.IsStatic); + Assert.False(c1M01Remove.IsAbstract); + Assert.False(c1M01Remove.IsVirtual); + Assert.False(c1M01Remove.IsMetadataVirtual()); + Assert.False(c1M01Remove.IsMetadataFinal); + Assert.False(c1M01Remove.IsMetadataNewSlot()); + + Assert.Empty(c1M01Remove.ExplicitInterfaceImplementations); + + if (c1M01.ContainingModule is PEModuleSymbol) + { + var c2M01Add = c3.FindImplementationForInterfaceMember(m01.AddMethod); + Assert.Equal("void C2.I1.add_M01(System.Action value)", c2M01Add.ToTestDisplayString()); + + var c2M01Remove = c3.FindImplementationForInterfaceMember(m01.RemoveMethod); + Assert.Equal("void C2.I1.remove_M01(System.Action value)", c2M01Remove.ToTestDisplayString()); + + // Forwarding methods for accessors aren't tied to an event + Assert.Null(c3.FindImplementationForInterfaceMember(m01)); + } + else + { + Assert.Same(c1M01, c3.FindImplementationForInterfaceMember(m01)); + Assert.Same(c1M01.AddMethod, c3.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Same(c1M01.RemoveMethod, c3.FindImplementationForInterfaceMember(m01.RemoveMethod)); + } + + var m02 = c3.Interfaces().Single().GetMembers("M02").OfType().Single(); + + var c2M02 = c3.BaseType().GetMember("I1.M02"); + Assert.Equal("event System.Action C2.I1.M02", c2M02.ToTestDisplayString()); + Assert.Same(c2M02, c3.FindImplementationForInterfaceMember(m02)); + Assert.Same(c2M02.AddMethod, c3.FindImplementationForInterfaceMember(m02.AddMethod)); + Assert.Same(c2M02.RemoveMethod, c3.FindImplementationForInterfaceMember(m02.RemoveMethod)); + } + } + + [Fact] + public void ImplementAbstractStaticEvent_16() + { + // A new implicit implementation is properly considered. + + var source1 = +@" +public interface I1 +{ + abstract static event System.Action M01; +} + +public class C1 : I1 +{ + public static event System.Action M01 { add{} remove => throw null; } +} + +public class C2 : C1 +{ + new public static event System.Action M01 { add{} remove => throw null; } +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C3 : C2, I1 +{ +} +"; + + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + var verifier = CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("C3.I1.add_M01", +@" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""void C2.M01.add"" + IL_0006: ret +} +"); + + verifier.VerifyIL("C3.I1.remove_M01", +@" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""void C2.M01.remove"" + IL_0006: ret +} +"); + } + } + + void validate(ModuleSymbol module) + { + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + var m01 = c3.Interfaces().Single().GetMembers().OfType().Single(); + + var c2M01 = c3.BaseType().GetMember("M01"); + var c2M01Add = c2M01.AddMethod; + var c2M01Remove = c2M01.RemoveMethod; + Assert.Equal("event System.Action C2.M01", c2M01.ToTestDisplayString()); + + Assert.True(c2M01.IsStatic); + Assert.False(c2M01.IsAbstract); + Assert.False(c2M01.IsVirtual); + Assert.Empty(c2M01.ExplicitInterfaceImplementations); + + Assert.True(c2M01Add.IsStatic); + Assert.False(c2M01Add.IsAbstract); + Assert.False(c2M01Add.IsVirtual); + Assert.False(c2M01Add.IsMetadataVirtual()); + Assert.False(c2M01Add.IsMetadataFinal); + Assert.False(c2M01Add.IsMetadataNewSlot()); + Assert.Empty(c2M01Add.ExplicitInterfaceImplementations); + + Assert.True(c2M01Remove.IsStatic); + Assert.False(c2M01Remove.IsAbstract); + Assert.False(c2M01Remove.IsVirtual); + Assert.False(c2M01Remove.IsMetadataVirtual()); + Assert.False(c2M01Remove.IsMetadataFinal); + Assert.False(c2M01Remove.IsMetadataNewSlot()); + Assert.Empty(c2M01Remove.ExplicitInterfaceImplementations); + + if (module is PEModuleSymbol) + { + var c3M01 = (EventSymbol)c3.FindImplementationForInterfaceMember(m01); + // Forwarding methods for accessors aren't tied to an event + Assert.Null(c3M01); + + var c3M01Add = (MethodSymbol)c3.FindImplementationForInterfaceMember(m01.AddMethod); + Assert.Equal("void C3.I1.add_M01(System.Action value)", c3M01Add.ToTestDisplayString()); + Assert.Same(m01.AddMethod, c3M01Add.ExplicitInterfaceImplementations.Single()); + + var c3M01Remove = (MethodSymbol)c3.FindImplementationForInterfaceMember(m01.RemoveMethod); + Assert.Equal("void C3.I1.remove_M01(System.Action value)", c3M01Remove.ToTestDisplayString()); + Assert.Same(m01.RemoveMethod, c3M01Remove.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Same(c2M01, c3.FindImplementationForInterfaceMember(m01)); + Assert.Same(c2M01Add, c3.FindImplementationForInterfaceMember(m01.AddMethod)); + Assert.Same(c2M01Remove, c3.FindImplementationForInterfaceMember(m01.RemoveMethod)); + } + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticEvent_19(bool genericFirst) + { + // An "ambiguity" in implicit/explicit implementation declared in generic base class. + + var generic = +@" + public static event System.Action M01 { add{} remove{} } +"; + var nonGeneric = +@" + static event System.Action I1.M01 { add{} remove{} } +"; + var source1 = +@" +public interface I1 +{ + abstract static event System.Action M01; +} + +public class C1 : I1 +{ +" + (genericFirst ? generic + nonGeneric : nonGeneric + generic) + @" +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { CreateCompilation("", targetFramework: TargetFramework.NetCoreApp).ToMetadataReference() }); + + Assert.Equal(2, compilation1.GlobalNamespace.GetTypeMember("C1").GetMembers().OfType().Where(m => m.Name.Contains("M01")).Count()); + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C2 : C1, I1 +{ +} +"; + + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } + + void validate(ModuleSymbol module) + { + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + var m01 = c2.Interfaces().Single().GetMembers().OfType().Single(); + + Assert.True(m01.ContainingModule is RetargetingModuleSymbol or PEModuleSymbol); + + var c1M01 = (EventSymbol)c2.FindImplementationForInterfaceMember(m01); + Assert.Equal("event System.Action C1.I1.M01", c1M01.OriginalDefinition.ToTestDisplayString()); + Assert.Same(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(c1M01, c2.BaseType().FindImplementationForInterfaceMember(m01)); + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticEvent_20(bool genericFirst) + { + // Same as ImplementAbstractStaticEvent_19 only interface is generic too. + + var generic = +@" + static event System.Action I1.M01 { add{} remove{} } +"; + var nonGeneric = +@" + public static event System.Action M01 { add{} remove{} } +"; + var source1 = +@" +public interface I1 +{ + abstract static event System.Action M01; +} + +public class C1 : I1 +{ +" + (genericFirst ? generic + nonGeneric : nonGeneric + generic) + @" +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { CreateCompilation("", targetFramework: TargetFramework.NetCoreApp).ToMetadataReference() }); + + Assert.Equal(2, compilation1.GlobalNamespace.GetTypeMember("C1").GetMembers().OfType().Where(m => m.Name.Contains("M01")).Count()); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C2 : C1, I1 +{ +} +"; + + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } + + void validate(ModuleSymbol module) + { + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + var m01 = c2.Interfaces().Single().GetMembers().OfType().Single(); + + Assert.True(m01.ContainingModule is RetargetingModuleSymbol or PEModuleSymbol); + + var c1M01 = (EventSymbol)c2.FindImplementationForInterfaceMember(m01); + Assert.Equal("event System.Action C1.I1.M01", c1M01.OriginalDefinition.ToTestDisplayString()); + Assert.Equal(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(c1M01, c2.BaseType().FindImplementationForInterfaceMember(m01)); + } + } } } From 5783ab14cc3b8fe65c8e2980bd66167f3c6c716e Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Fri, 7 May 2021 16:49:47 -0700 Subject: [PATCH 074/127] Add RemoteValueTrackingService --- eng/targets/Services.props | 1 + .../ValueTracking/IValueTrackingService.cs | 7 ++ .../SerializableValueTrackedItem.cs | 71 +++++++++++++++++++ .../Core/ValueTracking/ValueTrackedItem.cs | 16 +++-- .../ValueTracker.OperationCollector.cs | 4 +- .../Core/ValueTracking/ValueTracker.cs | 12 ++-- .../ValueTracking/ValueTrackingService.cs | 6 -- .../RemoteValueTrackingService.cs | 67 ++++++++++++++++- 8 files changed, 163 insertions(+), 21 deletions(-) create mode 100644 src/EditorFeatures/Core/ValueTracking/SerializableValueTrackedItem.cs diff --git a/eng/targets/Services.props b/eng/targets/Services.props index 3b21b7a3c07f5..623acb1b8e2ff 100644 --- a/eng/targets/Services.props +++ b/eng/targets/Services.props @@ -35,6 +35,7 @@ + diff --git a/src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs index 5e7d0e8c62e6f..16b207bf431f4 100644 --- a/src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.ValueTracking @@ -18,4 +19,10 @@ internal interface IValueTrackingService : IWorkspaceService Task> TrackValueSourceAsync(ValueTrackedItem previousTrackedItem, CancellationToken cancellationToken); Task TrackValueSourceAsync(ValueTrackedItem previousTrackedItem, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken); } + + internal interface IRemoteValueTrackingService + { + ValueTask> TrackValueSourceAsync(PinnedSolutionInfo solutionInfo, TextSpan selection, DocumentId document, CancellationToken cancellationToken); + ValueTask> TrackValueSourceAsync(PinnedSolutionInfo solutionInfo, SerializableValueTrackedItem previousTrackedItem, CancellationToken cancellationToken); + } } diff --git a/src/EditorFeatures/Core/ValueTracking/SerializableValueTrackedItem.cs b/src/EditorFeatures/Core/ValueTracking/SerializableValueTrackedItem.cs new file mode 100644 index 0000000000000..2db4dfdd779a4 --- /dev/null +++ b/src/EditorFeatures/Core/ValueTracking/SerializableValueTrackedItem.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ValueTracking +{ + [DataContract] + internal sealed class SerializableValueTrackedItem + { + [DataMember(Order = 0)] + public SerializableSymbolAndProjectId SymbolAndProjectId { get; } + + [DataMember(Order = 1)] + public SerializableValueTrackedItem? Parent { get; } + + [DataMember(Order = 2)] + public TextSpan TextSpan { get; } + + [DataMember(Order = 3)] + public DocumentId DocumentId { get; } + + public SerializableValueTrackedItem( + SerializableSymbolAndProjectId symbolAndProjectId, + TextSpan textSpan, + DocumentId documentId, + SerializableValueTrackedItem? parent = null) + { + SymbolAndProjectId = symbolAndProjectId; + Parent = parent; + TextSpan = textSpan; + DocumentId = documentId; + } + + public static SerializableValueTrackedItem Dehydrate(ValueTrackedItem valueTrackedItem, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var symbolAndProjectId = SerializableSymbolAndProjectId.Dehydrate(valueTrackedItem.Document.Project.Solution, valueTrackedItem.Symbol, cancellationToken); + var parent = valueTrackedItem.Parent is null + ? null + : Dehydrate(valueTrackedItem.Parent, cancellationToken); + + return new SerializableValueTrackedItem(symbolAndProjectId, valueTrackedItem.Span, valueTrackedItem.Document.Id, parent); + } + + public async Task RehydrateAsync(Solution solution, CancellationToken cancellationToken) + { + var document = solution.GetRequiredDocument(DocumentId); + + var symbol = await SymbolAndProjectId.TryRehydrateAsync(solution, cancellationToken).ConfigureAwait(false); + if (symbol is null) + { + return null; + } + + cancellationToken.ThrowIfCancellationRequested(); + var parent = Parent is null ? null : await Parent.RehydrateAsync(solution, cancellationToken).ConfigureAwait(false); + + return await ValueTrackedItem.TryCreateAsync(document, TextSpan, symbol, parent, cancellationToken).ConfigureAwait(false); + } + } +} diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs index ef90f3ba5490a..bc2694c7bf2b5 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs @@ -47,18 +47,23 @@ public override string ToString() return subText.ToString(); } - public static async Task TryCreateAsync(Solution solution, Location location, ISymbol symbol, ValueTrackedItem? parent = null, CancellationToken cancellationToken = default) + public static Task TryCreateAsync(Solution solution, Location location, ISymbol symbol, ValueTrackedItem? parent = null, CancellationToken cancellationToken = default) { Contract.ThrowIfNull(location.SourceTree); var document = solution.GetRequiredDocument(location.SourceTree); + return TryCreateAsync(document, location.SourceSpan, symbol, parent, cancellationToken); + } + + public static async Task TryCreateAsync(Document document, TextSpan textSpan, ISymbol symbol, ValueTrackedItem? parent = null, CancellationToken cancellationToken = default) + { var excerptService = document.Services.GetService(); SourceText? sourceText = null; ImmutableArray classifiedSpans = default; if (excerptService != null) { - var result = await excerptService.TryExcerptAsync(document, location.SourceSpan, ExcerptMode.SingleLine, cancellationToken).ConfigureAwait(false); + var result = await excerptService.TryExcerptAsync(document, textSpan, ExcerptMode.SingleLine, cancellationToken).ConfigureAwait(false); if (result.HasValue) { var value = result.Value; @@ -68,17 +73,18 @@ public override string ToString() if (sourceText is null) { - var documentSpan = await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync(document, location.SourceSpan, cancellationToken).ConfigureAwait(false); + var documentSpan = await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync(document, textSpan, cancellationToken).ConfigureAwait(false); var classificationResult = await ClassifiedSpansAndHighlightSpanFactory.ClassifyAsync(documentSpan, cancellationToken).ConfigureAwait(false); classifiedSpans = classificationResult.ClassifiedSpans; - sourceText = await location.SourceTree.GetTextAsync(cancellationToken).ConfigureAwait(false); + var syntaxTree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + sourceText = await syntaxTree.GetTextAsync(cancellationToken).ConfigureAwait(false); } return new ValueTrackedItem( symbol, sourceText, classifiedSpans, - location.SourceSpan, + textSpan, document, parent: parent); } diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTracker.OperationCollector.cs b/src/EditorFeatures/Core/ValueTracking/ValueTracker.OperationCollector.cs index 96d4234c5d623..87c09da0ac71d 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTracker.OperationCollector.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTracker.OperationCollector.cs @@ -43,8 +43,8 @@ IFieldReferenceOperation or IMethodBodyOperation methodBodyOperation => VisitReturnDescendentsAsync(methodBodyOperation, allowImplicit: true, cancellationToken), IBlockOperation blockOperation => VisitReturnDescendentsAsync(blockOperation, allowImplicit: false, cancellationToken), - // Default to reporting if there is symbol information available - _ => VisitDefaultAsync(operation, cancellationToken) + // Default to reporting if there is symbol information available + _ => VisitDefaultAsync(operation, cancellationToken) }; private async Task VisitReturnDescendentsAsync(IOperation operation, bool allowImplicit, CancellationToken cancellationToken) diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTracker.cs b/src/EditorFeatures/Core/ValueTracking/ValueTracker.cs index 8e62ece5859eb..d34db6b6855aa 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTracker.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTracker.cs @@ -2,15 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.CodeAnalysis.Shared.Extensions; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Text; -using System.Threading; +using System.Collections.Immutable; using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Operations; -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ValueTracking diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index b302a53c9677e..817b9f9e718dd 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -5,17 +5,11 @@ using System; using System.Collections.Immutable; using System.Composition; -using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.LanguageServices; -using Microsoft.CodeAnalysis.Operations; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ValueTracking { diff --git a/src/Workspaces/Remote/ServiceHub/Services/ValueTracking/RemoteValueTrackingService.cs b/src/Workspaces/Remote/ServiceHub/Services/ValueTracking/RemoteValueTrackingService.cs index 7099c762a4453..2dd89ae921dce 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/ValueTracking/RemoteValueTrackingService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/ValueTracking/RemoteValueTrackingService.cs @@ -4,11 +4,74 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; +using System.Reflection.Metadata; using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.ValueTracking; +using Microsoft.VisualStudio.Text; -namespace Microsoft.CodeAnalysis.Remote.Services.ValueTracking +namespace Microsoft.CodeAnalysis.Remote { - class RemoteValueTrackingService + internal sealed class RemoteValueTrackingService : BrokeredServiceBase, IRemoteValueTrackingService { + internal sealed class Factory : FactoryBase + { + protected override IRemoteValueTrackingService CreateService(in ServiceConstructionArguments arguments) + => new RemoteValueTrackingService(arguments); + } + + public RemoteValueTrackingService(ServiceConstructionArguments arguments) + : base(arguments) + { + } + + + public ValueTask> TrackValueSourceAsync(PinnedSolutionInfo solutionInfo, TextSpan selection, DocumentId documentId, CancellationToken cancellationToken) + { + return RunServiceAsync(async cancellationToken => + { + var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); + if (solution is null) + { + throw new InvalidOperationException(); + } + + var document = solution.GetRequiredDocument(documentId); + + var progress = new ValueTrackingProgressCollector(); + await ValueTracker.TrackValueSourceInternalAsync(selection, document, progress, cancellationToken).ConfigureAwait(false); + + var items = progress.GetItems(); + return items.SelectAsArray(item => SerializableValueTrackedItem.Dehydrate(item, cancellationToken)); + }, cancellationToken); + } + + public ValueTask> TrackValueSourceAsync(SerializableValueTrackedItem previousTrackedItem, PinnedSolutionInfo solutionInfo, CancellationToken cancellationToken) + { + return RunServiceAsync(async cancellationToken => + { + var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); + if (solution is null) + { + throw new InvalidOperationException(); + } + + var previousItem = await previousTrackedItem.RehydrateAsync(solution, cancellationToken).ConfigureAwait(false); + if (previousItem is null) + { + return ImmutableArray.Empty; + } + + var progress = new ValueTrackingProgressCollector(); + await ValueTracker.TrackValueSourceInternalAsync(previousItem, progress, cancellationToken).ConfigureAwait(false); + + var items = progress.GetItems(); + return items.SelectAsArray(item => SerializableValueTrackedItem.Dehydrate(item, cancellationToken)); + }, cancellationToken); + } } } From c38c32fc4113e6d5e84ef810b93d121c47334f5c Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Mon, 10 May 2021 16:57:53 -0700 Subject: [PATCH 075/127] Use remote client when possible. Remove dependency on ISymbol in UI --- .../ValueTracking/IValueTrackingService.cs | 5 +- .../SerializableValueTrackedItem.cs | 20 ++--- .../Core/ValueTracking/ValueTrackedItem.cs | 21 +++-- .../Core/ValueTracking/ValueTracker.cs | 28 ++++-- .../ValueTracking/ValueTrackingService.cs | 86 +++++++++++++------ .../AbstractBaseValueTrackingTests.cs | 3 +- .../ValueTrackedTreeItemViewModel.cs | 23 +++-- .../ValueTrackingCommandHandler.cs | 16 +++- .../ValueTrackingTreeItemViewModel.cs | 38 ++++---- .../RemoteValueTrackingService.cs | 14 ++- 10 files changed, 159 insertions(+), 95 deletions(-) diff --git a/src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs index 16b207bf431f4..7f21b4c70ef30 100644 --- a/src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs @@ -14,10 +14,7 @@ namespace Microsoft.CodeAnalysis.ValueTracking internal interface IValueTrackingService : IWorkspaceService { Task> TrackValueSourceAsync(TextSpan selection, Document document, CancellationToken cancellationToken); - Task TrackValueSourceAsync(TextSpan selection, Document document, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken); - - Task> TrackValueSourceAsync(ValueTrackedItem previousTrackedItem, CancellationToken cancellationToken); - Task TrackValueSourceAsync(ValueTrackedItem previousTrackedItem, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken); + Task> TrackValueSourceAsync(Solution solution, ValueTrackedItem previousTrackedItem, CancellationToken cancellationToken); } internal interface IRemoteValueTrackingService diff --git a/src/EditorFeatures/Core/ValueTracking/SerializableValueTrackedItem.cs b/src/EditorFeatures/Core/ValueTracking/SerializableValueTrackedItem.cs index 2db4dfdd779a4..f0ac530237b9f 100644 --- a/src/EditorFeatures/Core/ValueTracking/SerializableValueTrackedItem.cs +++ b/src/EditorFeatures/Core/ValueTracking/SerializableValueTrackedItem.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.ValueTracking internal sealed class SerializableValueTrackedItem { [DataMember(Order = 0)] - public SerializableSymbolAndProjectId SymbolAndProjectId { get; } + public SymbolKey SymbolKey { get; } [DataMember(Order = 1)] public SerializableValueTrackedItem? Parent { get; } @@ -29,35 +29,35 @@ internal sealed class SerializableValueTrackedItem public DocumentId DocumentId { get; } public SerializableValueTrackedItem( - SerializableSymbolAndProjectId symbolAndProjectId, + SymbolKey symbolKey, TextSpan textSpan, DocumentId documentId, SerializableValueTrackedItem? parent = null) { - SymbolAndProjectId = symbolAndProjectId; + SymbolKey = symbolKey; Parent = parent; TextSpan = textSpan; DocumentId = documentId; } - public static SerializableValueTrackedItem Dehydrate(ValueTrackedItem valueTrackedItem, CancellationToken cancellationToken) + public static SerializableValueTrackedItem Dehydrate(Solution solution, ValueTrackedItem valueTrackedItem, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - var symbolAndProjectId = SerializableSymbolAndProjectId.Dehydrate(valueTrackedItem.Document.Project.Solution, valueTrackedItem.Symbol, cancellationToken); var parent = valueTrackedItem.Parent is null ? null - : Dehydrate(valueTrackedItem.Parent, cancellationToken); + : Dehydrate(solution, valueTrackedItem.Parent, cancellationToken); - return new SerializableValueTrackedItem(symbolAndProjectId, valueTrackedItem.Span, valueTrackedItem.Document.Id, parent); + return new SerializableValueTrackedItem(valueTrackedItem.SymbolKey, valueTrackedItem.Span, valueTrackedItem.DocumentId, parent); } public async Task RehydrateAsync(Solution solution, CancellationToken cancellationToken) { var document = solution.GetRequiredDocument(DocumentId); + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var symbolResolution = SymbolKey.Resolve(semanticModel.Compilation, cancellationToken: cancellationToken); - var symbol = await SymbolAndProjectId.TryRehydrateAsync(solution, cancellationToken).ConfigureAwait(false); - if (symbol is null) + if (symbolResolution.Symbol is null) { return null; } @@ -65,7 +65,7 @@ public static SerializableValueTrackedItem Dehydrate(ValueTrackedItem valueTrack cancellationToken.ThrowIfCancellationRequested(); var parent = Parent is null ? null : await Parent.RehydrateAsync(solution, cancellationToken).ConfigureAwait(false); - return await ValueTrackedItem.TryCreateAsync(document, TextSpan, symbol, parent, cancellationToken).ConfigureAwait(false); + return await ValueTrackedItem.TryCreateAsync(document, TextSpan, symbolResolution.Symbol, parent, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs index bc2694c7bf2b5..5d4654fd52358 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs @@ -16,29 +16,31 @@ namespace Microsoft.CodeAnalysis.ValueTracking { internal class ValueTrackedItem { - public ISymbol Symbol { get; } + public SymbolKey SymbolKey { get; } public ValueTrackedItem? Parent { get; } - public Document Document { get; } + public DocumentId DocumentId { get; } public TextSpan Span { get; } public ImmutableArray ClassifiedSpans { get; } public SourceText SourceText { get; } + public Glyph Glyph { get; } private ValueTrackedItem( - ISymbol symbol, + SymbolKey symbolKey, SourceText sourceText, ImmutableArray classifiedSpans, TextSpan textSpan, - Document document, + DocumentId documentId, + Glyph glyph, ValueTrackedItem? parent = null) { - Symbol = symbol; + SymbolKey = symbolKey; Parent = parent; - + Glyph = glyph; Span = textSpan; ClassifiedSpans = classifiedSpans; SourceText = sourceText; - Document = document; + DocumentId = documentId; } public override string ToString() @@ -81,11 +83,12 @@ public override string ToString() } return new ValueTrackedItem( - symbol, + SymbolKey.Create(symbol, cancellationToken), sourceText, classifiedSpans, textSpan, - document, + document.Id, + symbol.GetGlyph(), parent: parent); } } diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTracker.cs b/src/EditorFeatures/Core/ValueTracking/ValueTracker.cs index d34db6b6855aa..09ea4bd18f4fb 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTracker.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTracker.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.ValueTracking { internal static partial class ValueTracker { - public static async Task TrackValueSourceInternalAsync( + public static async Task TrackValueSourceAsync( TextSpan selection, Document document, ValueTrackingProgressCollector progressCollector, @@ -71,29 +71,31 @@ or ILocalSymbol } } - public static async Task TrackValueSourceInternalAsync( + public static async Task TrackValueSourceAsync( + Solution solution, ValueTrackedItem previousTrackedItem, ValueTrackingProgressCollector progressCollector, CancellationToken cancellationToken) { progressCollector.Parent = previousTrackedItem; - var operationCollector = new OperationCollector(progressCollector, previousTrackedItem.Document.Project.Solution); + var operationCollector = new OperationCollector(progressCollector, solution); + var symbol = await GetSymbolAsync(previousTrackedItem, solution, cancellationToken).ConfigureAwait(false); - switch (previousTrackedItem.Symbol) + switch (symbol) { case ILocalSymbol: case IPropertySymbol: case IFieldSymbol: { // The "output" is a variable assignment, track places where it gets assigned and defined - await TrackVariableDefinitionsAsync(previousTrackedItem.Symbol, operationCollector, cancellationToken).ConfigureAwait(false); - await TrackVariableReferencesAsync(previousTrackedItem.Symbol, operationCollector, cancellationToken).ConfigureAwait(false); + await TrackVariableDefinitionsAsync(symbol, operationCollector, cancellationToken).ConfigureAwait(false); + await TrackVariableReferencesAsync(symbol, operationCollector, cancellationToken).ConfigureAwait(false); } break; case IParameterSymbol parameterSymbol: { - var previousSymbol = previousTrackedItem.Parent?.Symbol; + var previousSymbol = await GetSymbolAsync(previousTrackedItem.Parent, solution, cancellationToken).ConfigureAwait(false); // If the current parameter is a parameter symbol for the previous tracked method it should be treated differently. // For example: @@ -309,5 +311,17 @@ private static async Task TrackVariableDefinitionsAsync(ISymbol symbol, Operatio } } } + + private static async Task GetSymbolAsync(ValueTrackedItem? item, Solution solution, CancellationToken cancellationToken) + { + if (item is null) + { + return null; + } + + var document = solution.GetRequiredDocument(item.DocumentId); + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + return item.SymbolKey.Resolve(semanticModel.Compilation, cancellationToken: cancellationToken).Symbol; + } } } diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs index 817b9f9e718dd..091ac10716a21 100644 --- a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs +++ b/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs @@ -9,6 +9,8 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.ValueTracking @@ -28,45 +30,73 @@ public async Task> TrackValueSourceAsync( CancellationToken cancellationToken) { using var logger = Logger.LogBlock(FunctionId.ValueTracking_TrackValueSource, cancellationToken, LogLevel.Information); + var client = await RemoteHostClient.TryGetClientAsync(document.Project, cancellationToken).ConfigureAwait(false); + if (client != null) + { + var solution = document.Project.Solution; + + var result = await client.TryInvokeAsync>( + solution, + (service, solutionInfo, cancellationToken) => service.TrackValueSourceAsync(solutionInfo, selection, document.Id, cancellationToken), + cancellationToken).ConfigureAwait(false); + + if (!result.HasValue) + { + return ImmutableArray.Empty; + } + + _ = PooledObjects.ArrayBuilder.GetInstance(out var builder); + + foreach (var item in result.Value) + { + var rehydratedItem = await item.RehydrateAsync(document.Project.Solution, cancellationToken).ConfigureAwait(false); + builder.Add(rehydratedItem); + } + } + var progressTracker = new ValueTrackingProgressCollector(); - await ValueTracker.TrackValueSourceInternalAsync(selection, document, progressTracker, cancellationToken).ConfigureAwait(false); + await ValueTracker.TrackValueSourceAsync(selection, document, progressTracker, cancellationToken).ConfigureAwait(false); return progressTracker.GetItems(); } - public async Task TrackValueSourceAsync( - TextSpan selection, - Document document, - ValueTrackingProgressCollector progressCollector, - CancellationToken cancellationToken) - { - using var logger = Logger.LogBlock(FunctionId.ValueTracking_TrackValueSource, cancellationToken, LogLevel.Information); - await ValueTracker.TrackValueSourceInternalAsync( - selection, - document, - progressCollector, - cancellationToken).ConfigureAwait(false); - } - public async Task> TrackValueSourceAsync( + Solution solution, ValueTrackedItem previousTrackedItem, CancellationToken cancellationToken) { using var logger = Logger.LogBlock(FunctionId.ValueTracking_TrackValueSource, cancellationToken, LogLevel.Information); + var project = solution.GetRequiredProject(previousTrackedItem.DocumentId.ProjectId); + var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); + if (client != null) + { + var dehydratedItem = SerializableValueTrackedItem.Dehydrate(solution, previousTrackedItem, cancellationToken); + var result = await client.TryInvokeAsync>( + solution, + (service, solutionInfo, cancellationToken) => service.TrackValueSourceAsync(solutionInfo, dehydratedItem, cancellationToken), + cancellationToken).ConfigureAwait(false); + + if (!result.HasValue) + { + return ImmutableArray.Empty; + } + + _ = PooledObjects.ArrayBuilder.GetInstance(out var builder); + + foreach (var item in result.Value) + { + var rehydratedItem = await item.RehydrateAsync(solution, cancellationToken).ConfigureAwait(false); + if (rehydratedItem is null) + { + throw new InvalidOperationException(); + } + + builder.Add(rehydratedItem); + } + } + var progressTracker = new ValueTrackingProgressCollector(); - await ValueTracker.TrackValueSourceInternalAsync(previousTrackedItem, progressTracker, cancellationToken).ConfigureAwait(false); + await ValueTracker.TrackValueSourceAsync(solution, previousTrackedItem, progressTracker, cancellationToken).ConfigureAwait(false); return progressTracker.GetItems(); } - - public async Task TrackValueSourceAsync( - ValueTrackedItem previousTrackedItem, - ValueTrackingProgressCollector progressCollector, - CancellationToken cancellationToken) - { - using var logger = Logger.LogBlock(FunctionId.ValueTracking_TrackValueSource, cancellationToken, LogLevel.Information); - await ValueTracker.TrackValueSourceInternalAsync( - previousTrackedItem, - progressCollector, - cancellationToken).ConfigureAwait(false); - } } } diff --git a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs index 349a7bd3ec1b1..a7495e6c31dc0 100644 --- a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.Text; using Xunit; using System.Collections.Generic; -using Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking { @@ -31,7 +30,7 @@ internal static async Task> GetTrackedItemsAsyn internal static async Task> GetTrackedItemsAsync(TestWorkspace testWorkspace, ValueTrackedItem item, CancellationToken cancellationToken = default) { var service = testWorkspace.Services.GetRequiredService(); - return await service.TrackValueSourceAsync(item, cancellationToken); + return await service.TrackValueSourceAsync(testWorkspace.CurrentSolution, item, cancellationToken); } internal static async Task> ValidateItemsAsync(TestWorkspace testWorkspace, (int line, string text)[] itemInfo, CancellationToken cancellationToken = default) diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs index efdb5ede1bf4b..4a05e82158171 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Navigation; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.ValueTracking; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Shell; @@ -34,16 +35,19 @@ public ValueTrackedTreeItemViewModel( IGlyphService glyphService, IValueTrackingService valueTrackingService, IThreadingContext threadingContext, + string fileName, ImmutableArray children = default) : base( - trackedItem.Document, trackedItem.Span, trackedItem.SourceText, - trackedItem.Symbol, + trackedItem.DocumentId, + fileName, + trackedItem.Glyph, trackedItem.ClassifiedSpans, treeViewModel, glyphService, threadingContext, + solution.Workspace, children: children) { @@ -108,24 +112,24 @@ private void CalculateChildren() public override void Select() { - var workspace = Document.Project.Solution.Workspace; - var navigationService = workspace.Services.GetService(); + var navigationService = Workspace.Services.GetService(); if (navigationService is null) { return; } // While navigating do not activate the tab, which will change focus from the tool window - var options = workspace.Options + var options = Workspace.Options .WithChangedOption(new OptionKey(NavigationOptions.PreferProvisionalTab), true) .WithChangedOption(new OptionKey(NavigationOptions.ActivateTab), false); - navigationService.TryNavigateToSpan(workspace, Document.Id, _trackedItem.Span, options, ThreadingContext.DisposalToken); + navigationService.TryNavigateToSpan(Workspace, DocumentId, _trackedItem.Span, options, ThreadingContext.DisposalToken); } private async Task> CalculateChildrenAsync(CancellationToken cancellationToken) { var valueTrackedItems = await _valueTrackingService.TrackValueSourceAsync( + _solution, _trackedItem, cancellationToken).ConfigureAwait(false); @@ -133,13 +137,18 @@ private async Task> CalculateChil var builder = new List(); foreach (var valueTrackedItem in valueTrackedItems) { + var document = _solution.GetRequiredDocument(valueTrackedItem.DocumentId); + var fileName = document.FilePath ?? document.Name; + builder.Add(new ValueTrackedTreeItemViewModel( valueTrackedItem, _solution, TreeViewModel, _glyphService, _valueTrackingService, - ThreadingContext)); + ThreadingContext, + fileName + )); } return builder.ToImmutableArray(); diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs index e62db801d00c1..e249c9363e7c6 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs @@ -153,7 +153,7 @@ private async Task ShowToolWindowAsync(ITextView textView, ISymbol selectedSymbo var valueTrackingService = solution.Workspace.Services.GetRequiredService(); var classificationFormatMap = _classificationFormatMapService.GetClassificationFormatMap(textView); - var childItems = await valueTrackingService.TrackValueSourceAsync(item, cancellationToken).ConfigureAwait(false); + var childItems = await valueTrackingService.TrackValueSourceAsync(solution, item, cancellationToken).ConfigureAwait(false); var childViewModels = childItems.SelectAsArray(child => CreateViewModel(child)); RoslynDebug.AssertNotNull(location.SourceTree); @@ -164,14 +164,16 @@ private async Task ShowToolWindowAsync(ITextView textView, ISymbol selectedSymbo var classificationResult = await ClassifiedSpansAndHighlightSpanFactory.ClassifyAsync(documentSpan, cancellationToken).ConfigureAwait(false); var root = new ValueTrackingTreeItemViewModel( - document, location.SourceSpan, sourceText, - selectedSymbol, + document.Id, + document.FilePath ?? document.Name, + selectedSymbol.GetGlyph(), classificationResult.ClassifiedSpans, toolWindow.ViewModel, _glyphService, _threadingContext, + solution.Workspace, children: childViewModels); await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); @@ -182,14 +184,20 @@ private async Task ShowToolWindowAsync(ITextView textView, ISymbol selectedSymbo await ShowToolWindowAsync(cancellationToken).ConfigureAwait(true); ValueTrackingTreeItemViewModel CreateViewModel(ValueTrackedItem valueTrackedItem, ImmutableArray children = default) - => new ValueTrackedTreeItemViewModel( + { + var document = solution.GetRequiredDocument(valueTrackedItem.DocumentId); + var fileName = document.FilePath ?? document.Name; + + return new ValueTrackedTreeItemViewModel( valueTrackedItem, solution, toolWindow.ViewModel, _glyphService, valueTrackingService, _threadingContext, + fileName, children); + } } private async Task ShowToolWindowAsync(CancellationToken cancellationToken) diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs index d5a39de1d9a93..2e71d976aeba0 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs @@ -22,22 +22,23 @@ namespace Microsoft.VisualStudio.LanguageServices.ValueTracking { internal class ValueTrackingTreeItemViewModel : TreeViewItemBase { - private readonly SourceText _sourceText; - private readonly ISymbol _symbol; + private readonly string _sourceText; + private readonly Glyph _glyph; private readonly IGlyphService _glyphService; protected ValueTrackingTreeViewModel TreeViewModel { get; } protected TextSpan TextSpan { get; } protected LineSpan LineSpan { get; } - protected Document Document { get; } protected IThreadingContext ThreadingContext { get; } + protected DocumentId DocumentId { get; } + protected Workspace Workspace { get; } public int LineNumber => LineSpan.Start + 1; // LineSpan is 0 indexed, editors are not public ObservableCollection ChildItems { get; } = new(); - public string FileName => Document.FilePath ?? Document.Name; + public string FileName { get; } - public ImageSource GlyphImage => _symbol.GetGlyph().GetImageSource(_glyphService); + public ImageSource GlyphImage => _glyph.GetImageSource(_glyphService); public ImmutableArray ClassifiedSpans { get; } @@ -51,7 +52,11 @@ public IList Inlines } var classifiedTexts = ClassifiedSpans.SelectAsArray( - cs => new ClassifiedText(cs.ClassificationType, _sourceText.ToString(cs.TextSpan))); + cs => + { + var adjustedStart = cs.TextSpan.Start - TextSpan.Start; + return new ClassifiedText(cs.ClassificationType, _sourceText.Substring(adjustedStart, cs.TextSpan.Length)); + }); var spanStartPosition = TextSpan.Start - ClassifiedSpans[0].TextSpan.Start; var spanEndPosition = TextSpan.End - ClassifiedSpans[0].TextSpan.End; @@ -75,26 +80,30 @@ public IList Inlines } public ValueTrackingTreeItemViewModel( - Document document, TextSpan textSpan, SourceText sourceText, - ISymbol symbol, + DocumentId documentId, + string fileName, + Glyph glyph, ImmutableArray classifiedSpans, ValueTrackingTreeViewModel treeViewModel, IGlyphService glyphService, IThreadingContext threadingContext, + Workspace workspace, ImmutableArray children = default) { - Document = document; + FileName = fileName; TextSpan = textSpan; ClassifiedSpans = classifiedSpans; TreeViewModel = treeViewModel; ThreadingContext = threadingContext; - _sourceText = sourceText; - _symbol = symbol; + _sourceText = sourceText.ToString(TextSpan); + _glyph = glyph; _glyphService = glyphService; + Workspace = workspace; + DocumentId = documentId; if (!children.IsDefaultOrEmpty) { @@ -111,19 +120,18 @@ public ValueTrackingTreeItemViewModel( public virtual void Select() { - var workspace = Document.Project.Solution.Workspace; - var navigationService = workspace.Services.GetService(); + var navigationService = Workspace.Services.GetService(); if (navigationService is null) { return; } // While navigating do not activate the tab, which will change focus from the tool window - var options = workspace.Options + var options = Workspace.Options .WithChangedOption(new OptionKey(NavigationOptions.PreferProvisionalTab), true) .WithChangedOption(new OptionKey(NavigationOptions.ActivateTab), false); - navigationService.TryNavigateToLineAndOffset(workspace, Document.Id, LineSpan.Start, 0, options, ThreadingContext.DisposalToken); + navigationService.TryNavigateToLineAndOffset(Workspace, DocumentId, LineSpan.Start, 0, options, ThreadingContext.DisposalToken); } } } diff --git a/src/Workspaces/Remote/ServiceHub/Services/ValueTracking/RemoteValueTrackingService.cs b/src/Workspaces/Remote/ServiceHub/Services/ValueTracking/RemoteValueTrackingService.cs index 2dd89ae921dce..3ad451be7aab2 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/ValueTracking/RemoteValueTrackingService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/ValueTracking/RemoteValueTrackingService.cs @@ -3,16 +3,12 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Reflection.Metadata; -using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.ValueTracking; -using Microsoft.VisualStudio.Text; namespace Microsoft.CodeAnalysis.Remote { @@ -43,14 +39,14 @@ public ValueTask> TrackValueSourceA var document = solution.GetRequiredDocument(documentId); var progress = new ValueTrackingProgressCollector(); - await ValueTracker.TrackValueSourceInternalAsync(selection, document, progress, cancellationToken).ConfigureAwait(false); + await ValueTracker.TrackValueSourceAsync(selection, document, progress, cancellationToken).ConfigureAwait(false); var items = progress.GetItems(); - return items.SelectAsArray(item => SerializableValueTrackedItem.Dehydrate(item, cancellationToken)); + return items.SelectAsArray(item => SerializableValueTrackedItem.Dehydrate(solution, item, cancellationToken)); }, cancellationToken); } - public ValueTask> TrackValueSourceAsync(SerializableValueTrackedItem previousTrackedItem, PinnedSolutionInfo solutionInfo, CancellationToken cancellationToken) + public ValueTask> TrackValueSourceAsync(PinnedSolutionInfo solutionInfo, SerializableValueTrackedItem previousTrackedItem, CancellationToken cancellationToken) { return RunServiceAsync(async cancellationToken => { @@ -67,10 +63,10 @@ public ValueTask> TrackValueSourceA } var progress = new ValueTrackingProgressCollector(); - await ValueTracker.TrackValueSourceInternalAsync(previousItem, progress, cancellationToken).ConfigureAwait(false); + await ValueTracker.TrackValueSourceAsync(solution, previousItem, progress, cancellationToken).ConfigureAwait(false); var items = progress.GetItems(); - return items.SelectAsArray(item => SerializableValueTrackedItem.Dehydrate(item, cancellationToken)); + return items.SelectAsArray(item => SerializableValueTrackedItem.Dehydrate(solution, item, cancellationToken)); }, cancellationToken); } } From 1cffffa778094adbd2131334fdfd8810412b15c1 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Tue, 11 May 2021 16:06:39 -0700 Subject: [PATCH 076/127] Move ValueTracking to Features instead of EditorFeatures --- .../Core/Portable}/ValueTracking/IValueTrackingService.cs | 0 .../Core/Portable}/ValueTracking/SerializableValueTrackedItem.cs | 0 .../Core/Portable}/ValueTracking/ValueTrackedItem.cs | 0 .../ValueTracking/ValueTracker.FindReferencesProgress.cs | 0 .../Portable}/ValueTracking/ValueTracker.OperationCollector.cs | 0 .../Core => Features/Core/Portable}/ValueTracking/ValueTracker.cs | 0 .../Portable}/ValueTracking/ValueTrackingProgressCollector.cs | 0 .../Core/Portable}/ValueTracking/ValueTrackingService.cs | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename src/{EditorFeatures/Core => Features/Core/Portable}/ValueTracking/IValueTrackingService.cs (100%) rename src/{EditorFeatures/Core => Features/Core/Portable}/ValueTracking/SerializableValueTrackedItem.cs (100%) rename src/{EditorFeatures/Core => Features/Core/Portable}/ValueTracking/ValueTrackedItem.cs (100%) rename src/{EditorFeatures/Core => Features/Core/Portable}/ValueTracking/ValueTracker.FindReferencesProgress.cs (100%) rename src/{EditorFeatures/Core => Features/Core/Portable}/ValueTracking/ValueTracker.OperationCollector.cs (100%) rename src/{EditorFeatures/Core => Features/Core/Portable}/ValueTracking/ValueTracker.cs (100%) rename src/{EditorFeatures/Core => Features/Core/Portable}/ValueTracking/ValueTrackingProgressCollector.cs (100%) rename src/{EditorFeatures/Core => Features/Core/Portable}/ValueTracking/ValueTrackingService.cs (100%) diff --git a/src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs b/src/Features/Core/Portable/ValueTracking/IValueTrackingService.cs similarity index 100% rename from src/EditorFeatures/Core/ValueTracking/IValueTrackingService.cs rename to src/Features/Core/Portable/ValueTracking/IValueTrackingService.cs diff --git a/src/EditorFeatures/Core/ValueTracking/SerializableValueTrackedItem.cs b/src/Features/Core/Portable/ValueTracking/SerializableValueTrackedItem.cs similarity index 100% rename from src/EditorFeatures/Core/ValueTracking/SerializableValueTrackedItem.cs rename to src/Features/Core/Portable/ValueTracking/SerializableValueTrackedItem.cs diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs b/src/Features/Core/Portable/ValueTracking/ValueTrackedItem.cs similarity index 100% rename from src/EditorFeatures/Core/ValueTracking/ValueTrackedItem.cs rename to src/Features/Core/Portable/ValueTracking/ValueTrackedItem.cs diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTracker.FindReferencesProgress.cs b/src/Features/Core/Portable/ValueTracking/ValueTracker.FindReferencesProgress.cs similarity index 100% rename from src/EditorFeatures/Core/ValueTracking/ValueTracker.FindReferencesProgress.cs rename to src/Features/Core/Portable/ValueTracking/ValueTracker.FindReferencesProgress.cs diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTracker.OperationCollector.cs b/src/Features/Core/Portable/ValueTracking/ValueTracker.OperationCollector.cs similarity index 100% rename from src/EditorFeatures/Core/ValueTracking/ValueTracker.OperationCollector.cs rename to src/Features/Core/Portable/ValueTracking/ValueTracker.OperationCollector.cs diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTracker.cs b/src/Features/Core/Portable/ValueTracking/ValueTracker.cs similarity index 100% rename from src/EditorFeatures/Core/ValueTracking/ValueTracker.cs rename to src/Features/Core/Portable/ValueTracking/ValueTracker.cs diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingProgressCollector.cs b/src/Features/Core/Portable/ValueTracking/ValueTrackingProgressCollector.cs similarity index 100% rename from src/EditorFeatures/Core/ValueTracking/ValueTrackingProgressCollector.cs rename to src/Features/Core/Portable/ValueTracking/ValueTrackingProgressCollector.cs diff --git a/src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs b/src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs similarity index 100% rename from src/EditorFeatures/Core/ValueTracking/ValueTrackingService.cs rename to src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs From e478a73d2665a9c09c16b2ea9367c80c6fae7072 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Tue, 11 May 2021 17:22:00 -0700 Subject: [PATCH 077/127] Fix serialization. Move back to SourceText for now because Inlines need to be created on the UI thread but VM doesn't. Need to fix initial span in order to allow for string --- .../SerializableValueTrackedItem.cs | 9 +-- .../ValueTrackingTreeItemViewModel.cs | 75 +++++++++---------- .../Remote/Core/ServiceDescriptors.cs | 2 + 3 files changed, 41 insertions(+), 45 deletions(-) diff --git a/src/Features/Core/Portable/ValueTracking/SerializableValueTrackedItem.cs b/src/Features/Core/Portable/ValueTracking/SerializableValueTrackedItem.cs index f0ac530237b9f..e5840f18ea09e 100644 --- a/src/Features/Core/Portable/ValueTracking/SerializableValueTrackedItem.cs +++ b/src/Features/Core/Portable/ValueTracking/SerializableValueTrackedItem.cs @@ -2,14 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ValueTracking { @@ -20,13 +17,13 @@ internal sealed class SerializableValueTrackedItem public SymbolKey SymbolKey { get; } [DataMember(Order = 1)] - public SerializableValueTrackedItem? Parent { get; } + public TextSpan TextSpan { get; } [DataMember(Order = 2)] - public TextSpan TextSpan { get; } + public DocumentId DocumentId { get; } [DataMember(Order = 3)] - public DocumentId DocumentId { get; } + public SerializableValueTrackedItem? Parent { get; } public SerializableValueTrackedItem( SymbolKey symbolKey, diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs index 2e71d976aeba0..074294868dc2e 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingTreeItemViewModel.cs @@ -22,7 +22,7 @@ namespace Microsoft.VisualStudio.LanguageServices.ValueTracking { internal class ValueTrackingTreeItemViewModel : TreeViewItemBase { - private readonly string _sourceText; + private readonly SourceText _sourceText; private readonly Glyph _glyph; private readonly IGlyphService _glyphService; @@ -42,42 +42,7 @@ internal class ValueTrackingTreeItemViewModel : TreeViewItemBase public ImmutableArray ClassifiedSpans { get; } - public IList Inlines - { - get - { - if (ClassifiedSpans.IsDefaultOrEmpty) - { - return new List(); - } - - var classifiedTexts = ClassifiedSpans.SelectAsArray( - cs => - { - var adjustedStart = cs.TextSpan.Start - TextSpan.Start; - return new ClassifiedText(cs.ClassificationType, _sourceText.Substring(adjustedStart, cs.TextSpan.Length)); - }); - - var spanStartPosition = TextSpan.Start - ClassifiedSpans[0].TextSpan.Start; - var spanEndPosition = TextSpan.End - ClassifiedSpans[0].TextSpan.End; - - return classifiedTexts.ToInlines( - TreeViewModel.ClassificationFormatMap, - TreeViewModel.ClassificationTypeMap, - (run, classifiedText, position) => - { - if (TreeViewModel.HighlightBrush is not null) - { - if (position >= spanStartPosition && position <= spanEndPosition) - { - run.SetValue( - TextElement.BackgroundProperty, - TreeViewModel.HighlightBrush); - } - } - }); - } - } + public ImmutableArray Inlines => CalculateInlines(); public ValueTrackingTreeItemViewModel( TextSpan textSpan, @@ -94,12 +59,11 @@ public ValueTrackingTreeItemViewModel( { FileName = fileName; TextSpan = textSpan; - + _sourceText = sourceText; ClassifiedSpans = classifiedSpans; TreeViewModel = treeViewModel; ThreadingContext = threadingContext; - _sourceText = sourceText.ToString(TextSpan); _glyph = glyph; _glyphService = glyphService; Workspace = workspace; @@ -133,5 +97,38 @@ public virtual void Select() navigationService.TryNavigateToLineAndOffset(Workspace, DocumentId, LineSpan.Start, 0, options, ThreadingContext.DisposalToken); } + + private ImmutableArray CalculateInlines() + { + if (ClassifiedSpans.IsDefaultOrEmpty) + { + return ImmutableArray.Empty; + } + + var classifiedTexts = ClassifiedSpans.SelectAsArray( + cs => + { + return new ClassifiedText(cs.ClassificationType, _sourceText.ToString(cs.TextSpan)); + }); + + var spanStartPosition = TextSpan.Start - ClassifiedSpans[0].TextSpan.Start; + var spanEndPosition = TextSpan.End - ClassifiedSpans[0].TextSpan.End; + + return classifiedTexts.ToInlines( + TreeViewModel.ClassificationFormatMap, + TreeViewModel.ClassificationTypeMap, + (run, classifiedText, position) => + { + if (TreeViewModel.HighlightBrush is not null) + { + if (position >= spanStartPosition && position <= spanEndPosition) + { + run.SetValue( + TextElement.BackgroundProperty, + TreeViewModel.HighlightBrush); + } + } + }).ToImmutableArray(); + } } } diff --git a/src/Workspaces/Remote/Core/ServiceDescriptors.cs b/src/Workspaces/Remote/Core/ServiceDescriptors.cs index d252dbbc101cf..ed8b5be1f2c0f 100644 --- a/src/Workspaces/Remote/Core/ServiceDescriptors.cs +++ b/src/Workspaces/Remote/Core/ServiceDescriptors.cs @@ -25,6 +25,7 @@ using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SymbolSearch; using Microsoft.CodeAnalysis.TodoComments; +using Microsoft.CodeAnalysis.ValueTracking; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote @@ -69,6 +70,7 @@ internal sealed class ServiceDescriptors (typeof(IRemoteGlobalNotificationDeliveryService), null), (typeof(IRemoteCodeLensReferencesService), null), (typeof(IRemoteEditAndContinueService), typeof(IRemoteEditAndContinueService.ICallback)), + (typeof(IRemoteValueTrackingService), null), }); internal readonly RemoteSerializationOptions Options; From 7d97796179b774bf8907d5466e8077042a4a8b4e Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Wed, 12 May 2021 16:17:53 -0700 Subject: [PATCH 078/127] Add service descriptor in resx. Update tests to help with diagnosing this failure in the future --- src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs | 4 +++- src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx | 3 +++ .../Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.de.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.es.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.it.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf | 5 +++++ .../Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf | 5 +++++ 15 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs b/src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs index e963f875d7c52..74c052d76593d 100644 --- a/src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs +++ b/src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs @@ -170,7 +170,9 @@ internal void GetFeatureDisplayName(Type serviceInterface, ServiceDescriptor des Assert.NotNull(serviceInterface); var expectedName = descriptor32.GetFeatureDisplayName(); - Assert.NotEmpty(expectedName); + + // The service name couldn't be found. It may need to be added to RemoteWorkspacesResources.resx as FeatureName_{name} + Assert.False(string.IsNullOrEmpty(expectedName), $"Service name for '{serviceInterface.GetType()}' not available."); Assert.Equal(expectedName, descriptor64.GetFeatureDisplayName()); Assert.Equal(expectedName, descriptor64ServerGC.GetFeatureDisplayName()); diff --git a/src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx b/src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx index cba534f26f993..cffa5549d8477 100644 --- a/src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx +++ b/src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx @@ -198,4 +198,7 @@ Navigation bar + + Value Tracking + \ No newline at end of file diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf index e5c3c82490009..1bf3087c65d86 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf @@ -122,6 +122,11 @@ TODO zjišťování komentářů + + Value Tracking + Value Tracking + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' Kvůli přechodné chybě není funkce {0} momentálně k dispozici. Zkuste to prosím znovu později: {1} diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.de.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.de.xlf index f0a0e87181d5e..96198e84bded3 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.de.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.de.xlf @@ -122,6 +122,11 @@ TODO Kommentarermittlung + + Value Tracking + Value Tracking + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' Das Feature "{0}" ist aufgrund eines zeitweiligen Fehlers momentan nicht verfügbar. Versuchen Sie es später noch mal: "{1}" diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.es.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.es.xlf index 9d338ee5c0ceb..a1f760ba00c2e 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.es.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.es.xlf @@ -122,6 +122,11 @@ Detección de comentarios TODO + + Value Tracking + Value Tracking + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' La característica "{0}" no está disponible debido a un error intermitente; vuelva a intentarlo más tarde: "{1}" diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf index 71fdb37d25ed6..b869823099807 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf @@ -122,6 +122,11 @@ Détection de commentaires TODO + + Value Tracking + Value Tracking + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' La fonctionnalité '{0}' est non disponible en raison d'une erreur intermittente. Réessayez plus tard : '{1}' diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.it.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.it.xlf index 2b4cc09c11f96..22f6b518e3124 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.it.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.it.xlf @@ -122,6 +122,11 @@ Individuazione dei commenti TODO + + Value Tracking + Value Tracking + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' La funzionalità '{0}' non è attualmente disponibile a causa di un errore intermittente. Riprovare più tardi: '{1}' diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf index a62d85d722363..9a087d9c9e7ed 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf @@ -122,6 +122,11 @@ TODO コメント検出 + + Value Tracking + Value Tracking + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' 間欠的なエラーのため、機能 '{0}' は現在使用できません。後でもう一度お試しください: '{1}' diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf index 05bb722b52a19..750a2141c48cd 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf @@ -122,6 +122,11 @@ TODO 주석 검색 + + Value Tracking + Value Tracking + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' 일시적 오류로 인해 기능 '{0}'을(를) 현재 사용할 수 없습니다. 나중에 다시 시도하세요. '{1}' diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf index a191595d2c5a9..3be6404df7716 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf @@ -122,6 +122,11 @@ Odnajdywanie komentarzy TODO + + Value Tracking + Value Tracking + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' Funkcja „{0}” jest obecnie niedostępna z powodu sporadycznego błędu, spróbuj ponownie później: „{1}” diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf index cd7c71c1f2758..902cb1c957614 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf @@ -122,6 +122,11 @@ Descoberta de comentários TODO + + Value Tracking + Value Tracking + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' O recurso '{0}' não está disponível no momento devido a um erro intermitente. Faça uma nova tentativa mais tarde: '{1}' diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf index 144daa69fcb43..c2323fab4e6d5 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf @@ -122,6 +122,11 @@ Обнаружение комментариев TODO + + Value Tracking + Value Tracking + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' Функция "{0}" сейчас недоступна из-за временной ошибки. Повторите попытку позже: "{1}". diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf index 374b482966763..a412232b2856f 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf @@ -122,6 +122,11 @@ TODO açıklamalarını bulma + + Value Tracking + Value Tracking + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' '{0}' özelliği, aralıklı bir hata nedeniyle şu anda kullanılamıyor. Lütfen daha sonra yeniden deneyin: '{1}' diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf index b0458cf70ecbb..d6bf41fecd68c 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf @@ -122,6 +122,11 @@ TODO 注释发现 + + Value Tracking + Value Tracking + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' 功能“{0}”当前不可用,因为出现间歇性错误,请稍后重试:“{1}” diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf index 81b6b2b0818c0..ccc3a5323d24b 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf @@ -122,6 +122,11 @@ TODO 註解探索 + + Value Tracking + Value Tracking + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' 因為發生間歇性錯誤,所以 '{0}' 功能目前無法使用,請稍後再試: '{1}' From 684f5238a2bccba9e2daaebbb3b84f7c9ab1f83e Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Fri, 14 May 2021 10:20:16 -0700 Subject: [PATCH 079/127] Add support for abstract `==`/`!=` operators in interfaces. (#53393) --- .../Portable/Binder/Binder_Operators.cs | 31 +- .../BinaryOperatorOverloadResolution.cs | 6 +- .../CSharp/Portable/CSharpResources.resx | 2 +- .../Source/SourceMemberContainerSymbol.cs | 2 +- .../Source/SourceUserDefinedOperatorSymbol.cs | 2 +- .../SourceUserDefinedOperatorSymbolBase.cs | 21 +- .../Portable/xlf/CSharpResources.cs.xlf | 4 +- .../Portable/xlf/CSharpResources.de.xlf | 4 +- .../Portable/xlf/CSharpResources.es.xlf | 4 +- .../Portable/xlf/CSharpResources.fr.xlf | 4 +- .../Portable/xlf/CSharpResources.it.xlf | 4 +- .../Portable/xlf/CSharpResources.ja.xlf | 4 +- .../Portable/xlf/CSharpResources.ko.xlf | 4 +- .../Portable/xlf/CSharpResources.pl.xlf | 4 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 4 +- .../Portable/xlf/CSharpResources.ru.xlf | 4 +- .../Portable/xlf/CSharpResources.tr.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 4 +- .../DefaultInterfaceImplementationTests.cs | 81 +- .../StaticAbstractMembersInInterfacesTests.cs | 1278 +++++++++++++---- 21 files changed, 1154 insertions(+), 321 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index 324c11ee1c8e8..c7ce32b1dd2da 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -2207,23 +2207,30 @@ private bool CheckConstraintLanguageVersionAndRuntimeSupportForOperator(SyntaxNo { bool result = true; - if (methodOpt?.ContainingType?.IsInterface == true && methodOpt.IsStatic && methodOpt.IsAbstract) + if (methodOpt?.ContainingType?.IsInterface == true && methodOpt.IsStatic) { - if (constrainedToTypeOpt is not TypeParameterSymbol) + if (methodOpt.IsAbstract) { - Error(diagnostics, ErrorCode.ERR_BadAbstractStaticMemberAccess, node); - return false; - } - - if (Compilation.SourceModule != methodOpt.ContainingModule) - { - result = CheckFeatureAvailability(node, MessageID.IDS_FeatureStaticAbstractMembersInInterfaces, diagnostics); - - if (!Compilation.Assembly.RuntimeSupportsStaticAbstractMembersInInterfaces) + if (constrainedToTypeOpt is not TypeParameterSymbol) { - Error(diagnostics, ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, node); + Error(diagnostics, ErrorCode.ERR_BadAbstractStaticMemberAccess, node); return false; } + + if (Compilation.SourceModule != methodOpt.ContainingModule) + { + result = CheckFeatureAvailability(node, MessageID.IDS_FeatureStaticAbstractMembersInInterfaces, diagnostics); + + if (!Compilation.Assembly.RuntimeSupportsStaticAbstractMembersInInterfaces) + { + Error(diagnostics, ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, node); + return false; + } + } + } + else if (methodOpt.Name is WellKnownMemberNames.EqualityOperatorName or WellKnownMemberNames.InequalityOperatorName) + { + result = CheckFeatureAvailability(node, MessageID.IDS_FeatureStaticAbstractMembersInInterfaces, diagnostics); } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs index a1109ecbcf3c6..77c94f0db82a2 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs @@ -111,11 +111,7 @@ internal void BinaryOperatorOverloadResolution_NoEasyOut(BinaryOperatorKind kind Debug.Assert((result.Results.Count == 0) != hadApplicableCandidates); // If there are no applicable candidates in classes / stuctures, try with interface sources. - // From https://github.com/dotnet/csharplang/blob/main/meetings/2017/LDM-2017-06-27.md: - // We do not allow == and != and. Otherwise, there'd be no way to override Equals and GetHashCode, - // so you couldn't do == and != in a recommended way. - if (!hadApplicableCandidates && - kind != BinaryOperatorKind.Equal && kind != BinaryOperatorKind.NotEqual) + if (!hadApplicableCandidates) { result.Results.Clear(); diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 8658550a0f6a4..273b6dcd15849 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -1689,7 +1689,7 @@ If such a class is used as a base class and if the deriving class defines a dest The first operand of an overloaded shift operator must have the same type as the containing type, and the type of the second operand must be int - Interfaces cannot contain conversion, equality, or inequality operators + Conversion, equality, or inequality operators declared in interfaces must be abstract Structs cannot contain explicit parameterless constructors diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index f12c2a7b8ff9d..757410acaa573 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -3378,7 +3378,7 @@ private static void CheckInterfaceMember(Symbol member, BindingDiagnosticBag dia diagnostics.Add(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, member.Locations[0]); break; case MethodKind.UserDefinedOperator: - if (meth.Name == WellKnownMemberNames.EqualityOperatorName || meth.Name == WellKnownMemberNames.InequalityOperatorName) + if (!meth.IsAbstract && (meth.Name == WellKnownMemberNames.EqualityOperatorName || meth.Name == WellKnownMemberNames.InequalityOperatorName)) { diagnostics.Add(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, member.Locations[0]); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs index aa2cc845bf1d8..fdea2e0c68c07 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs @@ -67,7 +67,7 @@ private SourceUserDefinedOperatorSymbol( CheckForBlockAndExpressionBody( syntax.Body, syntax.ExpressionBody, syntax, diagnostics); - if (name != WellKnownMemberNames.EqualityOperatorName && name != WellKnownMemberNames.InequalityOperatorName) + if (IsAbstract || (name != WellKnownMemberNames.EqualityOperatorName && name != WellKnownMemberNames.InequalityOperatorName)) { CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasBody: syntax.Body != null || syntax.ExpressionBody != null, diagnostics: diagnostics); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs index ff60194e421ea..fed9496646932 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs @@ -48,9 +48,9 @@ protected SourceUserDefinedOperatorSymbolBase( this.MakeFlags(methodKind, declarationModifiers, returnsVoid: false, isExtensionMethod: false, isNullableAnalysisEnabled: isNullableAnalysisEnabled); if (this.ContainingType.IsInterface && - (methodKind == MethodKind.Conversion || name == WellKnownMemberNames.EqualityOperatorName || name == WellKnownMemberNames.InequalityOperatorName)) + (methodKind == MethodKind.Conversion || (!IsAbstract && (name == WellKnownMemberNames.EqualityOperatorName || name == WellKnownMemberNames.InequalityOperatorName)))) { - // If we have a conversion or equality/inequality operator in an interface, we already have reported that fact as + // If we have an unsupported conversion or equality/inequality operator in an interface, we already have reported that fact as // an error. No need to cascade the error further. return; } @@ -131,15 +131,19 @@ protected static DeclarationModifiers MakeDeclarationModifiers(MethodKind method if (inInterface) { - allowedModifiers |= DeclarationModifiers.Abstract | DeclarationModifiers.Sealed; + allowedModifiers |= DeclarationModifiers.Abstract; + + if (syntax is OperatorDeclarationSyntax { OperatorToken: var opToken1 } && opToken1.Kind() is not (SyntaxKind.EqualsEqualsToken or SyntaxKind.ExclamationEqualsToken)) + { + allowedModifiers |= DeclarationModifiers.Sealed; + } } } var result = ModifierUtils.MakeAndCheckNontypeMemberModifiers( syntax.Modifiers, defaultAccess, allowedModifiers, location, diagnostics, modifierErrors: out _); - if (inInterface && syntax is OperatorDeclarationSyntax { OperatorToken: var opToken } && - opToken.Kind() is not (SyntaxKind.EqualsEqualsToken or SyntaxKind.ExclamationEqualsToken)) + if (inInterface && syntax is OperatorDeclarationSyntax { OperatorToken: var opToken2 }) { if ((result & (DeclarationModifiers.Abstract | DeclarationModifiers.Sealed)) != 0) { @@ -163,7 +167,7 @@ protected static DeclarationModifiers MakeDeclarationModifiers(MethodKind method result &= ~DeclarationModifiers.Sealed; } - else if ((result & DeclarationModifiers.Static) != 0) + else if ((result & DeclarationModifiers.Static) != 0 && opToken2.Kind() is not (SyntaxKind.EqualsEqualsToken or SyntaxKind.ExclamationEqualsToken)) { Binder.CheckFeatureAvailability(location.SourceTree, MessageID.IDS_DefaultInterfaceImplementation, diagnostics, location); } @@ -243,10 +247,9 @@ protected override void MethodChecks(BindingDiagnosticBag diagnostics) MethodChecks(returnType, parameters, diagnostics); - // If we have a conversion/equality/inequality operator in an interface or static class then we already + // If we have a conversion operator in an interface or static class then we already // have reported that fact as an error. No need to cascade the error further. - if ((this.ContainingType.IsInterfaceType() && - (MethodKind == MethodKind.Conversion || Name == WellKnownMemberNames.EqualityOperatorName || Name == WellKnownMemberNames.InequalityOperatorName)) || + if ((this.ContainingType.IsInterfaceType() && MethodKind == MethodKind.Conversion) || this.ContainingType.IsStatic) { return; diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 4765fc9c653f2..0a2346499971a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -10938,8 +10938,8 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference - Interfaces cannot contain conversion, equality, or inequality operators - Rozhraní nemůžou obsahovat operátory převodu, rovnosti nebo nerovnosti. + Conversion, equality, or inequality operators declared in interfaces must be abstract + Rozhraní nemůžou obsahovat operátory převodu, rovnosti nebo nerovnosti. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index ab1a4a7fbd2d2..220e6777f859f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -10938,8 +10938,8 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett - Interfaces cannot contain conversion, equality, or inequality operators - Schnittstellen können keine Konvertierungs-, Gleichheits- oder Ungleichheitsoperatoren enthalten. + Conversion, equality, or inequality operators declared in interfaces must be abstract + Schnittstellen können keine Konvertierungs-, Gleichheits- oder Ungleichheitsoperatoren enthalten. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 069be52b4cd42..17f8eb9bf55b1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -10938,8 +10938,8 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe - Interfaces cannot contain conversion, equality, or inequality operators - Las interfaces no pueden contener operadores de conversión, igualdad o desigualdad + Conversion, equality, or inequality operators declared in interfaces must be abstract + Las interfaces no pueden contener operadores de conversión, igualdad o desigualdad diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index a9fabcbda47a2..9f6e436e91291 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -10938,8 +10938,8 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé - Interfaces cannot contain conversion, equality, or inequality operators - Les interfaces ne peuvent pas contenir d'opérateurs de conversion, d'égalité ou d'inégalité + Conversion, equality, or inequality operators declared in interfaces must be abstract + Les interfaces ne peuvent pas contenir d'opérateurs de conversion, d'égalité ou d'inégalité diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 47bff7a0ded9a..274d4003b19b9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -10938,8 +10938,8 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr - Interfaces cannot contain conversion, equality, or inequality operators - Le interfacce non possono contenere operatori di conversione, uguaglianza o disuguaglianza + Conversion, equality, or inequality operators declared in interfaces must be abstract + Le interfacce non possono contenere operatori di conversione, uguaglianza o disuguaglianza diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 07748317d60ab..aa37445063193 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -10938,8 +10938,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - Interfaces cannot contain conversion, equality, or inequality operators - インターフェイスには、変換演算子、等値演算子、非等値演算子を含めることはできません + Conversion, equality, or inequality operators declared in interfaces must be abstract + インターフェイスには、変換演算子、等値演算子、非等値演算子を含めることはできません diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 11c7b4d50b463..f63b2217d746d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -10937,8 +10937,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - Interfaces cannot contain conversion, equality, or inequality operators - 인터페이스에는 변환, 같음 또는 같지 않음 연산자가 포함될 수 없습니다. + Conversion, equality, or inequality operators declared in interfaces must be abstract + 인터페이스에는 변환, 같음 또는 같지 않음 연산자가 포함될 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index dec5265026e27..4c116038ff0ee 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -10938,8 +10938,8 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w - Interfaces cannot contain conversion, equality, or inequality operators - Interfejsy nie mogą zawierać operatorów konwersji, równości ani nierówności + Conversion, equality, or inequality operators declared in interfaces must be abstract + Interfejsy nie mogą zawierać operatorów konwersji, równości ani nierówności diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 739221aa1fd8c..cfc153ff69dfd 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -10938,8 +10938,8 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl - Interfaces cannot contain conversion, equality, or inequality operators - As interfaces não podem conter operadores de conversão, igualdade ou desigualdade + Conversion, equality, or inequality operators declared in interfaces must be abstract + As interfaces não podem conter operadores de conversão, igualdade ou desigualdade diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 3531c4b1c5d18..fe91f8188e175 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -10938,8 +10938,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - Interfaces cannot contain conversion, equality, or inequality operators - Интерфейсы не могут содержать операторы преобразования, проверки равенства и неравенства + Conversion, equality, or inequality operators declared in interfaces must be abstract + Интерфейсы не могут содержать операторы преобразования, проверки равенства и неравенства diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index a370147d536cf..f69fb0b01f293 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -10938,8 +10938,8 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T - Interfaces cannot contain conversion, equality, or inequality operators - Arabirimler dönüştürme, eşitlik veya eşitsizlik işleçleri içeremez + Conversion, equality, or inequality operators declared in interfaces must be abstract + Arabirimler dönüştürme, eşitlik veya eşitsizlik işleçleri içeremez diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index b10c0880a1b46..9f8a869bb52a6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -10943,8 +10943,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - Interfaces cannot contain conversion, equality, or inequality operators - 接口不能包含转换、相等或不相等运算符 + Conversion, equality, or inequality operators declared in interfaces must be abstract + 接口不能包含转换、相等或不相等运算符 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 7a31c929be076..c2b085d4f741d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -10938,8 +10938,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - Interfaces cannot contain conversion, equality, or inequality operators - 介面不能包含轉換、等式或不等式運算子 + Conversion, equality, or inequality operators declared in interfaces must be abstract + 介面不能包含轉換、等式或不等式運算子 diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs index 088dc75fc34f9..e652a27b09348 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs @@ -41138,33 +41138,46 @@ static void Main() "; var compilation1 = CreateCompilation(source1 + source2, options: TestOptions.DebugExe, targetFramework: TargetFramework.NetCoreApp, - parseOptions: TestOptions.Regular); + parseOptions: TestOptions.Regular9); compilation1.VerifyDiagnostics( - // (4,31): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // (4,31): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract // public static I1 operator ==(I1 x, I1 y) Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "==").WithLocation(4, 31), - // (10,31): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // (10,31): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract // public static I1 operator !=(I1 x, I1 y) Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "!=").WithLocation(10, 31), - // (24,13): error CS0029: Cannot implicitly convert type 'bool' to 'I1' + // (24,13): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // x = x == y; - Diagnostic(ErrorCode.ERR_NoImplicitConv, "x == y").WithArguments("bool", "I1").WithLocation(24, 13), - // (25,13): error CS0029: Cannot implicitly convert type 'bool' to 'I1' + Diagnostic(ErrorCode.ERR_FeatureInPreview, "x == y").WithArguments("static abstract members in interfaces").WithLocation(24, 13), + // (25,13): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // x = x != y; - Diagnostic(ErrorCode.ERR_NoImplicitConv, "x != y").WithArguments("bool", "I1").WithLocation(25, 13) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "x != y").WithArguments("static abstract members in interfaces").WithLocation(25, 13) + ); + + CreateCompilation(source1 + source2, options: TestOptions.DebugExe, targetFramework: TargetFramework.NetCoreApp, + parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (4,31): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // public static I1 operator ==(I1 x, I1 y) + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "==").WithLocation(4, 31), + // (10,31): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // public static I1 operator !=(I1 x, I1 y) + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "!=").WithLocation(10, 31) ); CompilationReference compilationReference = compilation1.ToMetadataReference(); var compilation2 = CreateCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, - parseOptions: TestOptions.Regular, targetFramework: TargetFramework.NetCoreApp); - compilation2.VerifyDiagnostics( - // (9,13): error CS0029: Cannot implicitly convert type 'bool' to 'I1' + parseOptions: TestOptions.RegularPreview, targetFramework: TargetFramework.NetCoreApp); + compilation2.VerifyDiagnostics(); + + CreateCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.NetCoreApp).VerifyDiagnostics( + // (9,13): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // x = x == y; - Diagnostic(ErrorCode.ERR_NoImplicitConv, "x == y").WithArguments("bool", "I1").WithLocation(9, 13), - // (10,13): error CS0029: Cannot implicitly convert type 'bool' to 'I1' + Diagnostic(ErrorCode.ERR_FeatureInPreview, "x == y").WithArguments("static abstract members in interfaces").WithLocation(9, 13), + // (10,13): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // x = x != y; - Diagnostic(ErrorCode.ERR_NoImplicitConv, "x != y").WithArguments("bool", "I1").WithLocation(10, 13) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "x != y").WithArguments("static abstract members in interfaces").WithLocation(10, 13) ); var ilSource = @" @@ -41211,14 +41224,19 @@ .locals init (class I1 V_0) } // end of class I1 "; - var compilation3 = CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugExe); - compilation3.VerifyDiagnostics( - // (9,13): error CS0029: Cannot implicitly convert type 'bool' to 'I1' + var compilation3 = CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularPreview); + CompileAndVerify(compilation3, verify: VerifyOnMonoOrCoreClr, expectedOutput: !ExecutionConditionUtil.IsMonoOrCoreClr ? null : @" +== +!= +").VerifyDiagnostics(); + + CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (9,13): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // x = x == y; - Diagnostic(ErrorCode.ERR_NoImplicitConv, "x == y").WithArguments("bool", "I1").WithLocation(9, 13), - // (10,13): error CS0029: Cannot implicitly convert type 'bool' to 'I1' + Diagnostic(ErrorCode.ERR_FeatureInPreview, "x == y").WithArguments("static abstract members in interfaces").WithLocation(9, 13), + // (10,13): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // x = x != y; - Diagnostic(ErrorCode.ERR_NoImplicitConv, "x != y").WithArguments("bool", "I1").WithLocation(10, 13) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "x != y").WithArguments("static abstract members in interfaces").WithLocation(10, 13) ); var source3 = @@ -41235,12 +41253,23 @@ static void Main() } } "; - var compilation4 = CreateCompilationWithIL(source3, ilSource, options: TestOptions.DebugExe); + var compilation4 = CreateCompilationWithIL(source3, ilSource, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularPreview); compilation4.VerifyDiagnostics(); CompileAndVerify(compilation4, verify: VerifyOnMonoOrCoreClr, expectedOutput: !ExecutionConditionUtil.IsMonoOrCoreClr ? null : @" -False -True +== +Test2 +!= +Test2 "); + + CreateCompilationWithIL(source3, ilSource, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (9,34): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // System.Console.WriteLine(x == y); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "x == y").WithArguments("static abstract members in interfaces").WithLocation(9, 34), + // (10,34): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // System.Console.WriteLine(x != y); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "x != y").WithArguments("static abstract members in interfaces").WithLocation(10, 34) + ); } [Fact] @@ -41272,10 +41301,10 @@ public static void Test(I1 x) var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, targetFramework: TargetFramework.NetCoreApp, parseOptions: TestOptions.Regular); compilation1.VerifyDiagnostics( - // (4,37): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // (4,37): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract // public static implicit operator int(I1 x) Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "int").WithLocation(4, 37), - // (8,37): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // (8,37): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract // public static explicit operator byte(I1 x) Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "byte").WithLocation(8, 37), // (15,17): error CS0029: Cannot implicitly convert type 'I1' to 'int' @@ -43688,10 +43717,10 @@ static explicit operator byte(I1 x) } compilation1.VerifyDiagnostics( - // (4,30): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // (4,30): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract // static implicit operator int(I1 x) Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "int").WithLocation(4, 30), - // (9,30): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // (9,30): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract // static explicit operator byte(I1 x) Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "byte").WithLocation(9, 30) ); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index 6849a7dd9d367..2c79f5877a5bb 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -3795,10 +3795,8 @@ public interface I1 ValidateOperatorModifiers_01(compilation1); } - [Theory] - [InlineData(false)] - [InlineData(true)] - public void OperatorModifiers_07(bool use7_3) + [Fact] + public void OperatorModifiers_07() { var source1 = @" @@ -3811,29 +3809,182 @@ public interface I1 public interface I2 { - sealed static bool operator== (I2 x, I2 y) {return false;} + sealed static bool operator== (I2 x, I2 y); sealed static bool operator!= (I2 x, I2 y) {return false;} } + +public interface I3 +{ + abstract sealed static bool operator== (I3 x, I3 y); + + abstract sealed static bool operator!= (I3 x, I3 y) {return false;} +} "; var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, - parseOptions: use7_3 ? TestOptions.Regular7_3 : TestOptions.RegularPreview, + parseOptions: TestOptions.Regular7_3, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,34): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract static bool operator== (I1 x, I1 y); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "==").WithArguments("abstract", "7.3", "preview").WithLocation(4, 34), + // (6,34): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract static bool operator!= (I1 x, I1 y) {return false;} + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "!=").WithArguments("abstract", "7.3", "preview").WithLocation(6, 34), + // (6,34): error CS0500: 'I1.operator !=(I1, I1)' cannot declare a body because it is marked abstract + // abstract static bool operator!= (I1 x, I1 y) {return false;} + Diagnostic(ErrorCode.ERR_AbstractHasBody, "!=").WithArguments("I1.operator !=(I1, I1)").WithLocation(6, 34), + // (11,32): error CS0106: The modifier 'sealed' is not valid for this item + // sealed static bool operator== (I2 x, I2 y); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "==").WithArguments("sealed").WithLocation(11, 32), + // (11,32): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // sealed static bool operator== (I2 x, I2 y); + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "==").WithLocation(11, 32), + // (13,32): error CS0106: The modifier 'sealed' is not valid for this item + // sealed static bool operator!= (I2 x, I2 y) {return false;} + Diagnostic(ErrorCode.ERR_BadMemberFlag, "!=").WithArguments("sealed").WithLocation(13, 32), + // (13,32): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // sealed static bool operator!= (I2 x, I2 y) {return false;} + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "!=").WithLocation(13, 32), + // (18,41): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static bool operator== (I3 x, I3 y); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "==").WithArguments("sealed").WithLocation(18, 41), + // (18,41): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract sealed static bool operator== (I3 x, I3 y); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "==").WithArguments("abstract", "7.3", "preview").WithLocation(18, 41), + // (20,41): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static bool operator!= (I3 x, I3 y) {return false;} + Diagnostic(ErrorCode.ERR_BadMemberFlag, "!=").WithArguments("sealed").WithLocation(20, 41), + // (20,41): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract sealed static bool operator!= (I3 x, I3 y) {return false;} + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "!=").WithArguments("abstract", "7.3", "preview").WithLocation(20, 41), + // (20,41): error CS0500: 'I3.operator !=(I3, I3)' cannot declare a body because it is marked abstract + // abstract sealed static bool operator!= (I3 x, I3 y) {return false;} + Diagnostic(ErrorCode.ERR_AbstractHasBody, "!=").WithArguments("I3.operator !=(I3, I3)").WithLocation(20, 41) + ); + + validate(); + + compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (4,34): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // (4,34): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. // abstract static bool operator== (I1 x, I1 y); - Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "==").WithLocation(4, 34), - // (6,34): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "==").WithArguments("abstract", "9.0", "preview").WithLocation(4, 34), + // (6,34): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static bool operator!= (I1 x, I1 y) {return false;} + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "!=").WithArguments("abstract", "9.0", "preview").WithLocation(6, 34), + // (6,34): error CS0500: 'I1.operator !=(I1, I1)' cannot declare a body because it is marked abstract + // abstract static bool operator!= (I1 x, I1 y) {return false;} + Diagnostic(ErrorCode.ERR_AbstractHasBody, "!=").WithArguments("I1.operator !=(I1, I1)").WithLocation(6, 34), + // (11,32): error CS0106: The modifier 'sealed' is not valid for this item + // sealed static bool operator== (I2 x, I2 y); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "==").WithArguments("sealed").WithLocation(11, 32), + // (11,32): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // sealed static bool operator== (I2 x, I2 y); + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "==").WithLocation(11, 32), + // (13,32): error CS0106: The modifier 'sealed' is not valid for this item + // sealed static bool operator!= (I2 x, I2 y) {return false;} + Diagnostic(ErrorCode.ERR_BadMemberFlag, "!=").WithArguments("sealed").WithLocation(13, 32), + // (13,32): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // sealed static bool operator!= (I2 x, I2 y) {return false;} + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "!=").WithLocation(13, 32), + // (18,41): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static bool operator== (I3 x, I3 y); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "==").WithArguments("sealed").WithLocation(18, 41), + // (18,41): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract sealed static bool operator== (I3 x, I3 y); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "==").WithArguments("abstract", "9.0", "preview").WithLocation(18, 41), + // (20,41): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static bool operator!= (I3 x, I3 y) {return false;} + Diagnostic(ErrorCode.ERR_BadMemberFlag, "!=").WithArguments("sealed").WithLocation(20, 41), + // (20,41): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract sealed static bool operator!= (I3 x, I3 y) {return false;} + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "!=").WithArguments("abstract", "9.0", "preview").WithLocation(20, 41), + // (20,41): error CS0500: 'I3.operator !=(I3, I3)' cannot declare a body because it is marked abstract + // abstract sealed static bool operator!= (I3 x, I3 y) {return false;} + Diagnostic(ErrorCode.ERR_AbstractHasBody, "!=").WithArguments("I3.operator !=(I3, I3)").WithLocation(20, 41) + ); + + validate(); + + compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (6,34): error CS0500: 'I1.operator !=(I1, I1)' cannot declare a body because it is marked abstract // abstract static bool operator!= (I1 x, I1 y) {return false;} - Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "!=").WithLocation(6, 34), - // (11,32): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators - // sealed static bool operator== (I2 x, I2 y) {return false;} + Diagnostic(ErrorCode.ERR_AbstractHasBody, "!=").WithArguments("I1.operator !=(I1, I1)").WithLocation(6, 34), + // (11,32): error CS0106: The modifier 'sealed' is not valid for this item + // sealed static bool operator== (I2 x, I2 y); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "==").WithArguments("sealed").WithLocation(11, 32), + // (11,32): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // sealed static bool operator== (I2 x, I2 y); Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "==").WithLocation(11, 32), - // (13,32): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // (13,32): error CS0106: The modifier 'sealed' is not valid for this item + // sealed static bool operator!= (I2 x, I2 y) {return false;} + Diagnostic(ErrorCode.ERR_BadMemberFlag, "!=").WithArguments("sealed").WithLocation(13, 32), + // (13,32): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract // sealed static bool operator!= (I2 x, I2 y) {return false;} - Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "!=").WithLocation(13, 32) + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "!=").WithLocation(13, 32), + // (18,41): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static bool operator== (I3 x, I3 y); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "==").WithArguments("sealed").WithLocation(18, 41), + // (20,41): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static bool operator!= (I3 x, I3 y) {return false;} + Diagnostic(ErrorCode.ERR_BadMemberFlag, "!=").WithArguments("sealed").WithLocation(20, 41), + // (20,41): error CS0500: 'I3.operator !=(I3, I3)' cannot declare a body because it is marked abstract + // abstract sealed static bool operator!= (I3 x, I3 y) {return false;} + Diagnostic(ErrorCode.ERR_AbstractHasBody, "!=").WithArguments("I3.operator !=(I3, I3)").WithLocation(20, 41) ); + + validate(); + + void validate() + { + foreach (MethodSymbol m01 in compilation1.GetTypeByMetadataName("I1").GetMembers()) + { + Assert.True(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.True(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(m01.ContainingType.FindImplementationForInterfaceMember(m01)); + } + + foreach (MethodSymbol m01 in compilation1.GetTypeByMetadataName("I2").GetMembers()) + { + Assert.False(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.False(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(m01.ContainingType.FindImplementationForInterfaceMember(m01)); + } + + foreach (MethodSymbol m01 in compilation1.GetTypeByMetadataName("I3").GetMembers()) + { + Assert.True(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.True(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(m01.ContainingType.FindImplementationForInterfaceMember(m01)); + } + } } [Theory] @@ -3862,16 +4013,22 @@ public interface I2 targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (4,39): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // (4,39): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract // abstract static implicit operator int(I1 x); Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "int").WithLocation(4, 39), - // (6,39): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // (6,39): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract // abstract static explicit operator bool(I1 x) {return false;} Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "bool").WithLocation(6, 39), - // (11,37): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // (11,37): error CS0106: The modifier 'sealed' is not valid for this item + // sealed static implicit operator int(I2 x) {return 0;} + Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("sealed").WithLocation(11, 37), + // (11,37): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract // sealed static implicit operator int(I2 x) {return 0;} Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "int").WithLocation(11, 37), - // (13,37): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // (13,37): error CS0106: The modifier 'sealed' is not valid for this item + // sealed static explicit operator bool(I2 x) {return false;} + Diagnostic(ErrorCode.ERR_BadMemberFlag, "bool").WithArguments("sealed").WithLocation(13, 37), + // (13,37): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract // sealed static explicit operator bool(I2 x) {return false;} Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "bool").WithLocation(13, 37) ); @@ -4257,6 +4414,8 @@ interface I1 abstract static I1 operator < (I1 x, I1 y); abstract static I1 operator >= (I1 x, I1 y); abstract static I1 operator <= (I1 x, I1 y); + abstract static I1 operator == (I1 x, I1 y); + abstract static I1 operator != (I1 x, I1 y); } "; var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, @@ -4282,7 +4441,7 @@ void validate(ModuleSymbol module) count++; } - Assert.Equal(6, count); + Assert.Equal(8, count); } } @@ -4336,6 +4495,8 @@ interface I1 abstract static I1 operator < (I1 x, I1 y); abstract static I1 operator >= (I1 x, I1 y); abstract static I1 operator <= (I1 x, I1 y); + abstract static I1 operator == (I1 x, I1 y); + abstract static I1 operator != (I1 x, I1 y); } "; var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, @@ -4360,7 +4521,13 @@ interface I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, ">=").WithLocation(8, 33), // (9,33): error CS9100: Target runtime doesn't support static abstract members in interfaces. // abstract static I1 operator <= (I1 x, I1 y); - Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "<=").WithLocation(9, 33) + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "<=").WithLocation(9, 33), + // (10,33): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static I1 operator == (I1 x, I1 y); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "==").WithLocation(10, 33), + // (11,33): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static I1 operator != (I1 x, I1 y); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "!=").WithLocation(11, 33) ); } @@ -6634,21 +6801,12 @@ static void M02(T x) where T : I1 } [Theory] - [InlineData("+")] - [InlineData("-")] - [InlineData("*")] - [InlineData("/")] - [InlineData("%")] - [InlineData("&")] - [InlineData("|")] - [InlineData("^")] - [InlineData("<<")] - [InlineData(">>")] - public void ConsumeAbstractBinaryOperator_01(string op) + [CombinatorialData] + public void ConsumeAbstractBinaryOperator_01([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op) { var source1 = @" -interface I1 +partial interface I1 { abstract static I1 operator" + op + @" (I1 x, int y); @@ -6676,6 +6834,20 @@ static void MT2() where T : I1 } } "; + + string matchingOp = MatchingBinaryOperator(op); + + if (matchingOp is object) + { + source1 += +@" +public partial interface I1 +{ + abstract static I1 operator" + matchingOp + @" (I1 x, int y); +} +"; + } + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, parseOptions: TestOptions.RegularPreview, targetFramework: TargetFramework.NetCoreApp); @@ -6926,17 +7098,8 @@ .maxstack 8 } [Theory] - [InlineData("+")] - [InlineData("-")] - [InlineData("*")] - [InlineData("/")] - [InlineData("%")] - [InlineData("&")] - [InlineData("|")] - [InlineData("^")] - [InlineData("<<")] - [InlineData(">>")] - public void ConsumeAbstractCompoundBinaryOperator_01(string op) + [CombinatorialData] + public void ConsumeAbstractCompoundBinaryOperator_01([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>")] string op) { var source1 = @" @@ -6993,45 +7156,104 @@ static void MT2() where T : I2 ); } + private static string BinaryOperatorKind(string op) + { + switch (op) + { + case "+": + return "Add"; + + case "-": + return "Subtract"; + + case "*": + return "Multiply"; + + case "/": + return "Divide"; + + case "%": + return "Remainder"; + + case "<<": + return "LeftShift"; + + case ">>": + return "RightShift"; + + case "&": + return "And"; + + case "|": + return "Or"; + + case "^": + return "ExclusiveOr"; + + case "<": + return "LessThan"; + + case "<=": + return "LessThanOrEqual"; + + case "==": + return "Equals"; + + case "!=": + return "NotEquals"; + + case ">=": + return "GreaterThanOrEqual"; + + case ">": + return "GreaterThan"; + + } + + throw TestExceptionUtilities.UnexpectedValue(op); + } + [Theory] - [InlineData("+", "op_Addition", "Add")] - [InlineData("-", "op_Subtraction", "Subtract")] - [InlineData("*", "op_Multiply", "Multiply")] - [InlineData("/", "op_Division", "Divide")] - [InlineData("%", "op_Modulus", "Remainder")] - [InlineData("&", "op_BitwiseAnd", "And")] - [InlineData("|", "op_BitwiseOr", "Or")] - [InlineData("^", "op_ExclusiveOr", "ExclusiveOr")] - [InlineData("<<", "op_LeftShift", "LeftShift")] - [InlineData(">>", "op_RightShift", "RightShift")] - public void ConsumeAbstractBinaryOperator_03(string op, string metadataName, string operatorKind) + [CombinatorialData] + public void ConsumeAbstractBinaryOperator_03([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>")] string op) { - bool isShiftOperator = op.Length == 2; + string metadataName = BinaryOperatorName(op); + + bool isShiftOperator = op is "<<" or ">>"; var source1 = @" -public interface I1 where T0 : I1 +public partial interface I1 where T0 : I1 +{ + abstract static T0 operator" + op + @" (T0 x, int a); +} + +partial class Test { + static void M03(T x) where T : U where U : I1 + { + _ = x " + op + @" 1; + } + + static void M05(T? y) where T : struct, U where U : I1 + { + _ = y " + op + @" 1; + } +} "; + if (!isShiftOperator) { source1 += @" +public partial interface I1 +{ abstract static T0 operator" + op + @" (int a, T0 x); abstract static T0 operator" + op + @" (I1 x, T0 a); abstract static T0 operator" + op + @" (T0 x, I1 a); -"; - } - - source1 += @" - abstract static T0 operator" + op + @" (T0 x, int a); } -class Test +partial class Test { -"; - if (!isShiftOperator) - { - source1 += @" static void M02(T x) where T : U where U : I1 { _ = 1 " + op + @" x; @@ -7051,21 +7273,10 @@ static void M07(T x, I1 y) where T : U where U : I1 { _ = x " + op + @" y; } +} "; } - source1 += @" - static void M03(T x) where T : U where U : I1 - { - _ = x " + op + @" 1; - } - - static void M05(T? y) where T : struct, U where U : I1 - { - _ = y " + op + @" 1; - } -} -"; var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, parseOptions: TestOptions.RegularPreview, targetFramework: TargetFramework.NetCoreApp); @@ -7155,29 +7366,451 @@ .maxstack 2 IL_000f: ret } "); - - verifier.VerifyIL("Test.M05(T?)", + + verifier.VerifyIL("Test.M05(T?)", +@" +{ + // Code size 35 (0x23) + .maxstack 2 + .locals init (T? V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloca.s V_0 + IL_0005: call ""readonly bool T?.HasValue.get"" + IL_000a: brtrue.s IL_000e + IL_000c: br.s IL_0022 + IL_000e: ldloca.s V_0 + IL_0010: call ""readonly T T?.GetValueOrDefault()"" + IL_0015: ldc.i4.1 + IL_0016: constrained. ""T"" + IL_001c: call ""T I1." + metadataName + @"(T, int)"" + IL_0021: pop + IL_0022: ret +} +"); + + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + if (!isShiftOperator) + { + verifier.VerifyIL("Test.M02(T)", +@" +{ + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldc.i4.1 + IL_0001: ldarg.0 + IL_0002: constrained. ""T"" + IL_0008: call ""T I1." + metadataName + @"(int, T)"" + IL_000d: pop + IL_000e: ret +} +"); + verifier.VerifyIL("Test.M04(T?)", +@" +{ + // Code size 32 (0x20) + .maxstack 2 + .locals init (T? V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""readonly bool T?.HasValue.get"" + IL_0009: brfalse.s IL_001f + IL_000b: ldc.i4.1 + IL_000c: ldloca.s V_0 + IL_000e: call ""readonly T T?.GetValueOrDefault()"" + IL_0013: constrained. ""T"" + IL_0019: call ""T I1." + metadataName + @"(int, T)"" + IL_001e: pop + IL_001f: ret +} +"); + verifier.VerifyIL("Test.M06(I1, T)", +@" +{ + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: constrained. ""T"" + IL_0008: call ""T I1." + metadataName + @"(I1, T)"" + IL_000d: pop + IL_000e: ret +} +"); + + verifier.VerifyIL("Test.M07(T, I1)", +@" +{ + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: constrained. ""T"" + IL_0008: call ""T I1." + metadataName + @"(T, I1)"" + IL_000d: pop + IL_000e: ret +} +"); + } + + verifier.VerifyIL("Test.M03(T)", +@" +{ + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: constrained. ""T"" + IL_0008: call ""T I1." + metadataName + @"(T, int)"" + IL_000d: pop + IL_000e: ret +} +"); + + verifier.VerifyIL("Test.M05(T?)", +@" +{ + // Code size 32 (0x20) + .maxstack 2 + .locals init (T? V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""readonly bool T?.HasValue.get"" + IL_0009: brfalse.s IL_001f + IL_000b: ldloca.s V_0 + IL_000d: call ""readonly T T?.GetValueOrDefault()"" + IL_0012: ldc.i4.1 + IL_0013: constrained. ""T"" + IL_0019: call ""T I1." + metadataName + @"(T, int)"" + IL_001e: pop + IL_001f: ret +} +"); + + var tree = compilation1.SyntaxTrees.Single(); + var model = compilation1.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().Where(n => n.ToString() == "x " + op + " 1").Single(); + + Assert.Equal("x " + op + " 1", node.ToString()); + VerifyOperationTreeForNode(compilation1, model, node, +// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" constraint is important for this operator, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? +@" +IBinaryOperation (BinaryOperatorKind." + BinaryOperatorKind(op) + @") (OperatorMethod: T I1." + metadataName + @"(T x, System.Int32 a)) (OperationKind.Binary, Type: T) (Syntax: 'x " + op + @" 1') + Left: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T) (Syntax: 'x') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') +"); + } + + [Theory] + [CombinatorialData] + public void ConsumeAbstractComparisonBinaryOperator_03([CombinatorialValues("<", ">", "<=", ">=", "==", "!=")] string op) + { + string metadataName = BinaryOperatorName(op); + + var source1 = +@" +public partial interface I1 where T0 : I1 +{ + abstract static bool operator" + op + @" (T0 x, int a); + abstract static bool operator" + op + @" (int a, T0 x); + abstract static bool operator" + op + @" (I1 x, T0 a); + abstract static bool operator" + op + @" (T0 x, I1 a); +} + +partial class Test +{ + static void M02(T x) where T : U where U : I1 + { + _ = 1 " + op + @" x; + } + + static void M03(T x) where T : U where U : I1 + { + _ = x " + op + @" 1; + } + + static void M06(I1 x, T y) where T : U where U : I1 + { + _ = x " + op + @" y; + } + + static void M07(T x, I1 y) where T : U where U : I1 + { + _ = x " + op + @" y; + } +} +"; + string matchingOp = MatchingBinaryOperator(op); + + source1 += +@" +public partial interface I1 +{ + abstract static bool operator" + matchingOp + @" (T0 x, int a); + abstract static bool operator" + matchingOp + @" (int a, T0 x); + abstract static bool operator" + matchingOp + @" (I1 x, T0 a); + abstract static bool operator" + matchingOp + @" (T0 x, I1 a); +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02(T)", +@" +{ + // Code size 16 (0x10) + .maxstack 2 + IL_0000: nop + IL_0001: ldc.i4.1 + IL_0002: ldarg.0 + IL_0003: constrained. ""T"" + IL_0009: call ""bool I1." + metadataName + @"(int, T)"" + IL_000e: pop + IL_000f: ret +} +"); + verifier.VerifyIL("Test.M06(I1, T)", +@" +{ + // Code size 16 (0x10) + .maxstack 2 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldarg.1 + IL_0003: constrained. ""T"" + IL_0009: call ""bool I1." + metadataName + @"(I1, T)"" + IL_000e: pop + IL_000f: ret +} +"); + + verifier.VerifyIL("Test.M07(T, I1)", +@" +{ + // Code size 16 (0x10) + .maxstack 2 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldarg.1 + IL_0003: constrained. ""T"" + IL_0009: call ""bool I1." + metadataName + @"(T, I1)"" + IL_000e: pop + IL_000f: ret +} +"); + + verifier.VerifyIL("Test.M03(T)", +@" +{ + // Code size 16 (0x10) + .maxstack 2 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldc.i4.1 + IL_0003: constrained. ""T"" + IL_0009: call ""bool I1." + metadataName + @"(T, int)"" + IL_000e: pop + IL_000f: ret +} +"); + + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02(T)", +@" +{ + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldc.i4.1 + IL_0001: ldarg.0 + IL_0002: constrained. ""T"" + IL_0008: call ""bool I1." + metadataName + @"(int, T)"" + IL_000d: pop + IL_000e: ret +} +"); + verifier.VerifyIL("Test.M06(I1, T)", +@" +{ + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: constrained. ""T"" + IL_0008: call ""bool I1." + metadataName + @"(I1, T)"" + IL_000d: pop + IL_000e: ret +} +"); + + verifier.VerifyIL("Test.M07(T, I1)", +@" +{ + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: constrained. ""T"" + IL_0008: call ""bool I1." + metadataName + @"(T, I1)"" + IL_000d: pop + IL_000e: ret +} +"); + + verifier.VerifyIL("Test.M03(T)", +@" +{ + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: constrained. ""T"" + IL_0008: call ""bool I1." + metadataName + @"(T, int)"" + IL_000d: pop + IL_000e: ret +} +"); + + var tree = compilation1.SyntaxTrees.Single(); + var model = compilation1.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().Where(n => n.ToString() == "x " + op + " 1").Single(); + + Assert.Equal("x " + op + " 1", node.ToString()); + VerifyOperationTreeForNode(compilation1, model, node, +// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" constraint is important for this operator, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? +@" +IBinaryOperation (BinaryOperatorKind." + BinaryOperatorKind(op) + @") (OperatorMethod: System.Boolean I1." + metadataName + @"(T x, System.Int32 a)) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'x " + op + @" 1') + Left: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T) (Syntax: 'x') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') +"); + } + + [Theory] + [CombinatorialData] + public void ConsumeAbstractLiftedComparisonBinaryOperator_03([CombinatorialValues("<", ">", "<=", ">=", "==", "!=")] string op) + { + string metadataName = BinaryOperatorName(op); + + var source1 = +@" +public partial interface I1 where T0 : I1 +{ + abstract static bool operator" + op + @" (T0 x, T0 a); +} + +partial class Test +{ + static void M04(T? x, T? y) where T : struct, U where U : I1 + { + _ = x " + op + @" y; + } +} +"; + string matchingOp = MatchingBinaryOperator(op); + + source1 += +@" +public partial interface I1 +{ + abstract static bool operator" + matchingOp + @" (T0 x, T0 a); +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + if (op is "==" or "!=") + { + verifier.VerifyIL("Test.M04(T?, T?)", +@" +{ + // Code size 61 (0x3d) + .maxstack 2 + .locals init (T? V_0, + T? V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldarg.1 + IL_0004: stloc.1 + IL_0005: ldloca.s V_0 + IL_0007: call ""readonly bool T?.HasValue.get"" + IL_000c: ldloca.s V_1 + IL_000e: call ""readonly bool T?.HasValue.get"" + IL_0013: beq.s IL_0017 + IL_0015: br.s IL_003c + IL_0017: ldloca.s V_0 + IL_0019: call ""readonly bool T?.HasValue.get"" + IL_001e: brtrue.s IL_0022 + IL_0020: br.s IL_003c + IL_0022: ldloca.s V_0 + IL_0024: call ""readonly T T?.GetValueOrDefault()"" + IL_0029: ldloca.s V_1 + IL_002b: call ""readonly T T?.GetValueOrDefault()"" + IL_0030: constrained. ""T"" + IL_0036: call ""bool I1." + metadataName + @"(T, T)"" + IL_003b: pop + IL_003c: ret +} +"); + } + else + { + verifier.VerifyIL("Test.M04(T?, T?)", @" { - // Code size 35 (0x23) + // Code size 51 (0x33) .maxstack 2 - .locals init (T? V_0) + .locals init (T? V_0, + T? V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 - IL_0003: ldloca.s V_0 - IL_0005: call ""readonly bool T?.HasValue.get"" - IL_000a: brtrue.s IL_000e - IL_000c: br.s IL_0022 - IL_000e: ldloca.s V_0 - IL_0010: call ""readonly T T?.GetValueOrDefault()"" - IL_0015: ldc.i4.1 - IL_0016: constrained. ""T"" - IL_001c: call ""T I1." + metadataName + @"(T, int)"" - IL_0021: pop - IL_0022: ret + IL_0003: ldarg.1 + IL_0004: stloc.1 + IL_0005: ldloca.s V_0 + IL_0007: call ""readonly bool T?.HasValue.get"" + IL_000c: ldloca.s V_1 + IL_000e: call ""readonly bool T?.HasValue.get"" + IL_0013: and + IL_0014: brtrue.s IL_0018 + IL_0016: br.s IL_0032 + IL_0018: ldloca.s V_0 + IL_001a: call ""readonly T T?.GetValueOrDefault()"" + IL_001f: ldloca.s V_1 + IL_0021: call ""readonly T T?.GetValueOrDefault()"" + IL_0026: constrained. ""T"" + IL_002c: call ""bool I1." + metadataName + @"(T, T)"" + IL_0031: pop + IL_0032: ret } "); + } compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview, @@ -7185,120 +7818,85 @@ .locals init (T? V_0) verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); - if (!isShiftOperator) + if (op is "==" or "!=") { - verifier.VerifyIL("Test.M02(T)", -@" -{ - // Code size 15 (0xf) - .maxstack 2 - IL_0000: ldc.i4.1 - IL_0001: ldarg.0 - IL_0002: constrained. ""T"" - IL_0008: call ""T I1." + metadataName + @"(int, T)"" - IL_000d: pop - IL_000e: ret -} -"); - verifier.VerifyIL("Test.M04(T?)", + verifier.VerifyIL("Test.M04(T?, T?)", @" { - // Code size 32 (0x20) + // Code size 56 (0x38) .maxstack 2 - .locals init (T? V_0) + .locals init (T? V_0, + T? V_1) IL_0000: ldarg.0 IL_0001: stloc.0 - IL_0002: ldloca.s V_0 - IL_0004: call ""readonly bool T?.HasValue.get"" - IL_0009: brfalse.s IL_001f - IL_000b: ldc.i4.1 - IL_000c: ldloca.s V_0 - IL_000e: call ""readonly T T?.GetValueOrDefault()"" - IL_0013: constrained. ""T"" - IL_0019: call ""T I1." + metadataName + @"(int, T)"" - IL_001e: pop - IL_001f: ret -} -"); - verifier.VerifyIL("Test.M06(I1, T)", -@" -{ - // Code size 15 (0xf) - .maxstack 2 - IL_0000: ldarg.0 - IL_0001: ldarg.1 - IL_0002: constrained. ""T"" - IL_0008: call ""T I1." + metadataName + @"(I1, T)"" - IL_000d: pop - IL_000e: ret + IL_0002: ldarg.1 + IL_0003: stloc.1 + IL_0004: ldloca.s V_0 + IL_0006: call ""readonly bool T?.HasValue.get"" + IL_000b: ldloca.s V_1 + IL_000d: call ""readonly bool T?.HasValue.get"" + IL_0012: bne.un.s IL_0037 + IL_0014: ldloca.s V_0 + IL_0016: call ""readonly bool T?.HasValue.get"" + IL_001b: brfalse.s IL_0037 + IL_001d: ldloca.s V_0 + IL_001f: call ""readonly T T?.GetValueOrDefault()"" + IL_0024: ldloca.s V_1 + IL_0026: call ""readonly T T?.GetValueOrDefault()"" + IL_002b: constrained. ""T"" + IL_0031: call ""bool I1." + metadataName + @"(T, T)"" + IL_0036: pop + IL_0037: ret } "); - verifier.VerifyIL("Test.M07(T, I1)", -@" -{ - // Code size 15 (0xf) - .maxstack 2 - IL_0000: ldarg.0 - IL_0001: ldarg.1 - IL_0002: constrained. ""T"" - IL_0008: call ""T I1." + metadataName + @"(T, I1)"" - IL_000d: pop - IL_000e: ret -} -"); } - - verifier.VerifyIL("Test.M03(T)", -@" -{ - // Code size 15 (0xf) - .maxstack 2 - IL_0000: ldarg.0 - IL_0001: ldc.i4.1 - IL_0002: constrained. ""T"" - IL_0008: call ""T I1." + metadataName + @"(T, int)"" - IL_000d: pop - IL_000e: ret -} -"); - - verifier.VerifyIL("Test.M05(T?)", + else + { + verifier.VerifyIL("Test.M04(T?, T?)", @" { - // Code size 32 (0x20) + // Code size 48 (0x30) .maxstack 2 - .locals init (T? V_0) + .locals init (T? V_0, + T? V_1) IL_0000: ldarg.0 IL_0001: stloc.0 - IL_0002: ldloca.s V_0 - IL_0004: call ""readonly bool T?.HasValue.get"" - IL_0009: brfalse.s IL_001f - IL_000b: ldloca.s V_0 - IL_000d: call ""readonly T T?.GetValueOrDefault()"" - IL_0012: ldc.i4.1 - IL_0013: constrained. ""T"" - IL_0019: call ""T I1." + metadataName + @"(T, int)"" - IL_001e: pop - IL_001f: ret + IL_0002: ldarg.1 + IL_0003: stloc.1 + IL_0004: ldloca.s V_0 + IL_0006: call ""readonly bool T?.HasValue.get"" + IL_000b: ldloca.s V_1 + IL_000d: call ""readonly bool T?.HasValue.get"" + IL_0012: and + IL_0013: brfalse.s IL_002f + IL_0015: ldloca.s V_0 + IL_0017: call ""readonly T T?.GetValueOrDefault()"" + IL_001c: ldloca.s V_1 + IL_001e: call ""readonly T T?.GetValueOrDefault()"" + IL_0023: constrained. ""T"" + IL_0029: call ""bool I1." + metadataName + @"(T, T)"" + IL_002e: pop + IL_002f: ret } "); + } var tree = compilation1.SyntaxTrees.Single(); var model = compilation1.GetSemanticModel(tree); - var node = tree.GetRoot().DescendantNodes().OfType().Where(n => n.ToString() == "x " + op + " 1").Single(); + var node = tree.GetRoot().DescendantNodes().OfType().Where(n => n.ToString() == "x " + op + " y").Single(); - Assert.Equal("x " + op + " 1", node.ToString()); + Assert.Equal("x " + op + " y", node.ToString()); VerifyOperationTreeForNode(compilation1, model, node, // PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" constraint is important for this operator, but it is not // reflected in the IOperation tree. Should we change the shape of the tree in order // to expose this information? @" -IBinaryOperation (BinaryOperatorKind." + operatorKind + @") (OperatorMethod: T I1." + metadataName + @"(T x, System.Int32 a)) (OperationKind.Binary, Type: T) (Syntax: 'x " + op + @" 1') +IBinaryOperation (BinaryOperatorKind." + BinaryOperatorKind(op) + @", IsLifted) (OperatorMethod: System.Boolean I1." + metadataName + @"(T x, T a)) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'x " + op + @" y') Left: - IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T) (Syntax: 'x') + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T?) (Syntax: 'x') Right: - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: T?) (Syntax: 'y') "); } @@ -8111,17 +8709,8 @@ .locals init (T? V_0, } [Theory] - [InlineData("+")] - [InlineData("-")] - [InlineData("*")] - [InlineData("/")] - [InlineData("%")] - [InlineData("&")] - [InlineData("|")] - [InlineData("^")] - [InlineData("<<")] - [InlineData(">>")] - public void ConsumeAbstractBinaryOperator_04(string op) + [CombinatorialData] + public void ConsumeAbstractBinaryOperator_04([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op) { var source1 = @" @@ -8159,7 +8748,7 @@ static void M02(T x, int y) where T : I1 parseOptions: TestOptions.RegularPreview, targetFramework: TargetFramework.DesktopLatestExtended); - compilation3.VerifyDiagnostics( + compilation3.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( // (12,32): error CS9100: Target runtime doesn't support static abstract members in interfaces. // abstract static I1 operator- (I1 x, int y); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(12, 32) @@ -8311,17 +8900,8 @@ static void M02(T x, int y) where T : I1 } [Theory] - [InlineData("+")] - [InlineData("-")] - [InlineData("*")] - [InlineData("/")] - [InlineData("%")] - [InlineData("&")] - [InlineData("|")] - [InlineData("^")] - [InlineData("<<")] - [InlineData(">>")] - public void ConsumeAbstractBinaryOperator_06(string op) + [CombinatorialData] + public void ConsumeAbstractBinaryOperator_06([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op) { var source1 = @" @@ -8359,7 +8939,7 @@ static void M02(T x, int y) where T : I1 parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.NetCoreApp); - compilation3.VerifyDiagnostics( + compilation3.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( // (12,32): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. // abstract static I1 operator- (I1 x, int y); Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, op).WithArguments("abstract", "9.0", "preview").WithLocation(12, 32) @@ -12746,7 +13326,7 @@ public interface I2 where T : I2 [Theory] [CombinatorialData] - public void ImplementAbstractStaticBinaryOperator_01([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op, bool structure) + public void ImplementAbstractStaticBinaryOperator_01([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op, bool structure) { var typeKeyword = structure ? "struct" : "class"; @@ -12827,7 +13407,7 @@ public interface I2 where T : I2 parseOptions: TestOptions.RegularPreview, targetFramework: TargetFramework.NetCoreApp); - compilation1.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + compilation1.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_OperatorNeedsMatch or (int)ErrorCode.WRN_EqualityOpWithoutEquals or (int)ErrorCode.WRN_EqualityOpWithoutGetHashCode)).Verify( // (8,10): error CS0535: 'C1' does not implement interface member 'I1.operator >>(C1, int)' // C1 : I1 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.operator " + op + "(C1, int)").WithLocation(8, 10), @@ -13047,7 +13627,7 @@ interface I14 : I1 [Theory] [CombinatorialData] - public void ImplementAbstractStaticBinaryOperator_03([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op) + public void ImplementAbstractStaticBinaryOperator_03([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op) { var source1 = @" @@ -13128,10 +13708,7 @@ interface I14 : I1 ErrorCode badSignatureError = isShift ? ErrorCode.ERR_BadShiftOperatorSignature : ErrorCode.ERR_BadBinaryOperatorSignature; ErrorCode badAbstractSignatureError = isShift ? ErrorCode.ERR_BadAbstractShiftOperatorSignature : ErrorCode.ERR_BadAbstractBinaryOperatorSignature; - compilation1.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( - // (12,17): error CS0558: User-defined operator 'I3.operator |(I1, int)' must be declared static and public - // I1 operator |(I1 x, int y) => default; - Diagnostic(ErrorCode.ERR_OperatorsMustBeStatic, op).WithArguments("I3.operator " + op + "(I1, int)").WithLocation(12, 17), + var expected = new[] { // (12,17): error CS0563: One of the parameters of a binary operator must be the containing type // I1 operator |(I1 x, int y) => default; Diagnostic(badSignatureError, op).WithLocation(12, 17), @@ -13150,9 +13727,6 @@ interface I14 : I1 // (32,33): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. // abstract static I1 operator |(I1 x, int y); Diagnostic(badAbstractSignatureError, op).WithLocation(32, 33), - // (42,16): error CS0558: User-defined operator 'I8.operator |(T, int)' must be declared static and public - // T operator |(T x, int y) => default; - Diagnostic(ErrorCode.ERR_OperatorsMustBeStatic, op).WithArguments("I8.operator " + op + "(T, int)").WithLocation(42, 16), // (42,16): error CS0563: One of the parameters of a binary operator must be the containing type // T operator |(T x, int y) => default; Diagnostic(badSignatureError, op).WithLocation(42, 16), @@ -13180,7 +13754,42 @@ interface I14 : I1 // (67,36): error CS0539: 'I14.operator |(I1, int)' in explicit interface declaration is not found among members of the interface that can be implemented // abstract static I1 I1.operator |(I1 x, int y); Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("I14.operator " + op + "(I1, int)").WithLocation(67, 36) - ); + }; + + if (op is "==" or "!=") + { + expected = expected.Concat( + new[] { + // (12,17): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // I1 operator ==(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, op).WithLocation(12, 17), + // (17,24): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // static I1 operator ==(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, op).WithLocation(17, 24), + // (42,16): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // T operator ==(T x, int y) => default; + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, op).WithLocation(42, 16), + // (47,23): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // static T operator ==(T x, int y) => default; + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, op).WithLocation(47, 23), + } + ).ToArray(); + } + else + { + expected = expected.Concat( + new[] { + // (12,17): error CS0558: User-defined operator 'I3.operator |(I1, int)' must be declared static and public + // I1 operator |(I1 x, int y) => default; + Diagnostic(ErrorCode.ERR_OperatorsMustBeStatic, op).WithArguments("I3.operator " + op + "(I1, int)").WithLocation(12, 17), + // (42,16): error CS0558: User-defined operator 'I8.operator |(T, int)' must be declared static and public + // T operator |(T x, int y) => default; + Diagnostic(ErrorCode.ERR_OperatorsMustBeStatic, op).WithArguments("I8.operator " + op + "(T, int)").WithLocation(42, 16) + } + ).ToArray(); + } + + compilation1.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify(expected); var m01 = compilation1.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); @@ -13273,7 +13882,7 @@ public interface I2 where T : I2 [Theory] [CombinatorialData] - public void ImplementAbstractStaticBinaryOperator_04([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op, bool structure) + public void ImplementAbstractStaticBinaryOperator_04([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op, bool structure) { var typeKeyword = structure ? "struct" : "class"; @@ -13310,7 +13919,7 @@ public interface I2 where T : I2 targetFramework: TargetFramework.NetCoreApp, references: new[] { compilation1.ToMetadataReference() }); - compilation2.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + compilation2.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_OperatorNeedsMatch or (int)ErrorCode.WRN_EqualityOpWithoutEquals or (int)ErrorCode.WRN_EqualityOpWithoutGetHashCode)).Verify( // (4,15): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // static I1 I1.operator +(I1 x, int y) => default; Diagnostic(ErrorCode.ERR_FeatureInPreview, "I1.").WithArguments("static abstract members in interfaces").WithLocation(4, 15) @@ -13320,7 +13929,7 @@ public interface I2 where T : I2 parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.NetCoreApp); - compilation3.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + compilation3.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_OperatorNeedsMatch or (int)ErrorCode.WRN_EqualityOpWithoutEquals or (int)ErrorCode.WRN_EqualityOpWithoutGetHashCode)).Verify( // (4,15): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // static I1 I1.operator +(I1 x, int y) => default; Diagnostic(ErrorCode.ERR_FeatureInPreview, "I1.").WithArguments("static abstract members in interfaces").WithLocation(4, 15), @@ -13384,7 +13993,7 @@ public interface I1 where T : I1 [Theory] [CombinatorialData] - public void ImplementAbstractStaticBinaryOperator_05([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op, bool structure) + public void ImplementAbstractStaticBinaryOperator_05([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op, bool structure) { var typeKeyword = structure ? "struct" : "class"; @@ -13411,7 +14020,7 @@ public interface I1 where T : I1 targetFramework: TargetFramework.DesktopLatestExtended, references: new[] { compilation1.ToMetadataReference() }); - compilation2.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + compilation2.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_OperatorNeedsMatch or (int)ErrorCode.WRN_EqualityOpWithoutEquals or (int)ErrorCode.WRN_EqualityOpWithoutGetHashCode)).Verify( // (2,12): error CS9110: 'Test1.operator >>(Test1, int)' cannot implement interface member 'I1.operator >>(Test1, int)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.operator " + op + "(Test1, int)", "I1.operator " + op + "(Test1, int)", "Test1").WithLocation(2, 12) @@ -13421,7 +14030,7 @@ public interface I1 where T : I1 parseOptions: TestOptions.RegularPreview, targetFramework: TargetFramework.DesktopLatestExtended); - compilation3.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + compilation3.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_OperatorNeedsMatch or (int)ErrorCode.WRN_EqualityOpWithoutEquals or (int)ErrorCode.WRN_EqualityOpWithoutGetHashCode)).Verify( // (2,12): error CS9110: 'Test1.operator >>(Test1, int)' cannot implement interface member 'I1.operator >>(Test1, int)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.operator " + op + "(Test1, int)", "I1.operator " + op + "(Test1, int)", "Test1").WithLocation(2, 12), @@ -13482,7 +14091,7 @@ public interface I1 [Theory] [CombinatorialData] - public void ImplementAbstractStaticBinaryOperator_06([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op, bool structure) + public void ImplementAbstractStaticBinaryOperator_06([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op, bool structure) { var typeKeyword = structure ? "struct" : "class"; @@ -13676,7 +14285,7 @@ void validate(ModuleSymbol module) [Theory] [CombinatorialData] - public void ImplementAbstractStaticBinaryOperator_07([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op, bool structure) + public void ImplementAbstractStaticBinaryOperator_07([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op, bool structure) { // Basic implicit implementation scenario, MethodImpl is emitted @@ -13689,6 +14298,8 @@ public partial interface I1 where T : I1 abstract static T operator " + op + @"(T x, int y); } +#pragma warning disable CS0660, CS0661 // 'C1' defines operator == or operator != but does not override Object.Equals(object o)/Object.GetHashCode() + partial " + typeKeyword + @" C : I1 { @@ -13908,7 +14519,7 @@ void validate(ModuleSymbol module) [Theory] [CombinatorialData] - public void ImplementAbstractStaticBinaryOperator_08([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op, bool structure) + public void ImplementAbstractStaticBinaryOperator_08([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op, bool structure) { // Basic explicit implementation scenario @@ -14126,7 +14737,7 @@ void validate(ModuleSymbol module) [Theory] [CombinatorialData] - public void ImplementAbstractStaticBinaryOperator_09([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op) + public void ImplementAbstractStaticBinaryOperator_09([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op) { // Explicit implementation from base is treated as an implementation @@ -14326,7 +14937,7 @@ public class C5 : C2, I1 [Theory] [CombinatorialData] - public void ImplementAbstractStaticBinaryOperator_10([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op) + public void ImplementAbstractStaticBinaryOperator_10([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op) { // Implicit implementation is considered only for types implementing interface in source. // In metadata, only explicit implementations are considered @@ -14525,7 +15136,7 @@ public class C1 : I1 [Theory] [CombinatorialData] - public void ImplementAbstractStaticBinaryOperator_11([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op) + public void ImplementAbstractStaticBinaryOperator_11([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op) { // Ignore invalid metadata (non-abstract static virtual method). @@ -14662,7 +15273,7 @@ public class C1 : I2 [Theory] [CombinatorialData] - public void ImplementAbstractStaticBinaryOperator_12([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op) + public void ImplementAbstractStaticBinaryOperator_12([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op) { // Ignore invalid metadata (default interface implementation for a static method) @@ -14725,7 +15336,7 @@ public class C1 : I2 [Theory] [CombinatorialData] - public void ImplementAbstractStaticBinaryOperator_13([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=")] string op) + public void ImplementAbstractStaticBinaryOperator_13([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=", "==", "!=")] string op) { // A forwarding method is added for an implicit implementation declared in base class. @@ -14736,6 +15347,8 @@ public partial interface I1 where T : I1 abstract static T operator " + op + @"(T x, C1 y); } +#pragma warning disable CS0660, CS0661 // 'C1' defines operator == or operator != but does not override Object.Equals(object o)/Object.GetHashCode() + public partial class C1 { public static C2 operator " + op + @"(C2 x, C1 y) => default; @@ -15054,12 +15667,12 @@ .maxstack 1 private static string MatchingBinaryOperator(string op) { - return op switch { "<" => ">", ">" => "<", "<=" => ">=", ">=" => "<=", _ => null }; + return op switch { "<" => ">", ">" => "<", "<=" => ">=", ">=" => "<=", "==" => "!=", "!=" => "==", _ => null }; } [Theory] [CombinatorialData] - public void ImplementAbstractStaticBinaryOperator_14([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op) + public void ImplementAbstractStaticBinaryOperator_14([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op) { // A forwarding method is added for an implicit implementation with modopt mismatch. @@ -15091,6 +15704,8 @@ int32 y var source1 = @" +#pragma warning disable CS0660, CS0661 // 'C1' defines operator == or operator != but does not override Object.Equals(object o)/Object.GetHashCode() + class C1 : I1 { public static C1 operator " + op + @"(C1 x, int y) => default; @@ -15304,7 +15919,7 @@ void validate(ModuleSymbol module) [Theory] [CombinatorialData] - public void ImplementAbstractStaticBinaryOperator_15([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=")] string op) + public void ImplementAbstractStaticBinaryOperator_15([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=", "==", "!=")] string op) { // A forwarding method isn't created if base class implements interface exactly the same way. @@ -15316,6 +15931,8 @@ public partial interface I1 where T : I1 abstract static T operator " + op + @"(T x, C2 y); } +#pragma warning disable CS0660, CS0661 // 'C1' defines operator == or operator != but does not override Object.Equals(object o)/Object.GetHashCode() + public partial class C1 { public static C2 operator " + op + @"(C2 x, C1 y) => default; @@ -15418,7 +16035,7 @@ void validate(ModuleSymbol module) [Theory] [CombinatorialData] - public void ImplementAbstractStaticBinaryOperator_16([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=")] string op) + public void ImplementAbstractStaticBinaryOperator_16([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=", "==", "!=")] string op) { // A new implicit implementation is properly considered. @@ -15429,6 +16046,8 @@ public partial interface I1 where T : I1 abstract static T operator " + op + @"(T x, C1 y); } +#pragma warning disable CS0660, CS0661 // 'C1' defines operator == or operator != but does not override Object.Equals(object o)/Object.GetHashCode() + public partial class C1 : I1 { public static C2 operator " + op + @"(C2 x, C1 y) => default; @@ -15533,7 +16152,7 @@ void validate(ModuleSymbol module) [Theory] [CombinatorialData] - public void ImplementAbstractStaticBinaryOperator_18([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=")] string op, bool genericFirst) + public void ImplementAbstractStaticBinaryOperator_18([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=", "==", "!=")] string op, bool genericFirst) { // An "ambiguity" in implicit implementation declared in generic base class plus interface is generic too. @@ -15552,6 +16171,8 @@ public partial interface I1 where T : I1 abstract static T operator " + op + @"(T x, U y); } +#pragma warning disable CS0660, CS0661 // 'C1' defines operator == or operator != but does not override Object.Equals(object o)/Object.GetHashCode() + public partial class C1 : I1, U> { " + (genericFirst ? generic + nonGeneric : nonGeneric + generic) + @" @@ -15635,7 +16256,7 @@ void validate(ModuleSymbol module) [Theory] [CombinatorialData] - public void ImplementAbstractStaticBinaryOperator_20([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=")] string op, bool genericFirst) + public void ImplementAbstractStaticBinaryOperator_20([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=", "==", "!=")] string op, bool genericFirst) { // Same as ImplementAbstractStaticBinaryOperator_18 only implementation is explicit in source. @@ -15654,6 +16275,8 @@ public partial interface I1 where T : I1 abstract static T operator " + op + @"(T x, U y); } +#pragma warning disable CS0660, CS0661 // 'C1' defines operator == or operator != but does not override Object.Equals(object o)/Object.GetHashCode() + public partial class C1 : I1, U> { " + (genericFirst ? generic + nonGeneric : nonGeneric + generic) + @" @@ -15725,7 +16348,7 @@ void validate(ModuleSymbol module) [Theory] [CombinatorialData] - public void ImplementAbstractStaticBinaryOperator_22([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=")] string op, bool genericFirst) + public void ImplementAbstractStaticBinaryOperator_22([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=", "==", "!=")] string op, bool genericFirst) { // Same as ImplementAbstractStaticMethod_18 only implicit implementation is in an intermediate base. @@ -15744,6 +16367,8 @@ public partial interface I1 where T : I1 abstract static T operator " + op + @"(T x, U y); } +#pragma warning disable CS0660, CS0661 // 'C1' defines operator == or operator != but does not override Object.Equals(object o)/Object.GetHashCode() + public partial class C1 { " + (genericFirst ? generic + nonGeneric : nonGeneric + generic) + @" @@ -15981,7 +16606,7 @@ public interface I1 [Theory] [CombinatorialData] - public void ExplicitImplementationModifiersBinaryOperator_01([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op) + public void ExplicitImplementationModifiersBinaryOperator_01([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op) { var source1 = @" @@ -16176,7 +16801,7 @@ class C2 : I1 [Theory] [CombinatorialData] - public void ExplicitInterfaceSpecifierErrorsBinaryOperator_01([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=")] string op) + public void ExplicitInterfaceSpecifierErrorsBinaryOperator_01([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op) { var source1 = @" @@ -18189,7 +18814,7 @@ int32 get_M01 () cil managed .method public hidebysig specialname abstract virtual static void modopt(I1) set_M01 ( - int32 'value' + int32 modopt(I1) 'value' ) cil managed { } @@ -18197,7 +18822,7 @@ void modopt(I1) set_M01 ( .property int32 M01() { .get int32 I1::get_M01() - .set void modopt(I1) I1::set_M01(int32) + .set void modopt(I1) I1::set_M01(int32 modopt(I1)) } } @@ -18288,7 +18913,7 @@ void validate(ModuleSymbol module) c1M01Set = (MethodSymbol)c1.FindImplementationForInterfaceMember(m01.SetMethod); Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c1M01Set.MethodKind); - Assert.Equal("void modopt(I1) C1.I1.set_M01(System.Int32 value)", c1M01Set.ToTestDisplayString()); + Assert.Equal("void modopt(I1) C1.I1.set_M01(System.Int32 modopt(I1) value)", c1M01Set.ToTestDisplayString()); Assert.Same(m01.SetMethod, c1M01Set.ExplicitInterfaceImplementations.Single()); Assert.True(c1M01Set.IsStatic); @@ -18336,6 +18961,7 @@ void validate(ModuleSymbol module) Assert.False(c2M01Set.IsMetadataNewSlot()); Assert.Equal(MethodKind.PropertySet, c2M01Set.MethodKind); Assert.Equal("void modopt(I1) C2.I1.M01.set", c2M01Set.ToTestDisplayString()); + Assert.Equal("System.Int32 modopt(I1) value", c2M01Set.Parameters.Single().ToTestDisplayString()); Assert.Same(m01.SetMethod, c2M01Set.ExplicitInterfaceImplementations.Single()); Assert.Same(c2M01Set, c2.FindImplementationForInterfaceMember(m01.SetMethod)); @@ -20572,6 +21198,29 @@ class [mscorlib]System.Action`1 'value' .removeon void I1::remove_M01(class [mscorlib]System.Action`1) } } + +.class interface public auto ansi abstract I2 +{ + .method public hidebysig specialname abstract virtual static + void add_M02 ( + class [mscorlib]System.Action modopt(I1) 'value' + ) cil managed + { + } + + .method public hidebysig specialname abstract virtual static + void modopt(I2) remove_M02 ( + class [mscorlib]System.Action 'value' + ) cil managed + { + } + + .event class [mscorlib]System.Action M02 + { + .addon void I2::add_M02(class [mscorlib]System.Action modopt(I1)) + .removeon void modopt(I2) I2::remove_M02(class [mscorlib]System.Action) + } +} "; var source1 = @@ -20585,6 +21234,18 @@ class C2 : I1 { static event System.Action I1.M01 { add => throw null; remove{} } } + +#pragma warning disable CS0067 // The event 'C3.M02' is never used + +class C3 : I2 +{ + public static event System.Action M02; +} + +class C4 : I2 +{ + static event System.Action I2.M02 { add => throw null; remove{} } +} "; var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, @@ -20705,6 +21366,121 @@ void validate(ModuleSymbol module) Assert.Same(c2M01, c2.GetMembers().OfType().Single()); Assert.Equal(2, c2.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + var m02 = c3.Interfaces().Single().GetMembers().OfType().Single(); + + var c3M02 = c3.GetMembers().OfType().Single(); + var c3M02Add = c3M02.AddMethod; + var c3M02Remove = c3M02.RemoveMethod; + + Assert.Equal("event System.Action C3.M02", c3M02.ToTestDisplayString()); + Assert.Empty(c3M02.ExplicitInterfaceImplementations); + Assert.True(c3M02.IsStatic); + Assert.False(c3M02.IsAbstract); + Assert.False(c3M02.IsVirtual); + + Assert.Equal(MethodKind.EventAdd, c3M02Add.MethodKind); + Assert.Equal("void C3.M02.add", c3M02Add.ToTestDisplayString()); + Assert.Equal("System.Action value", c3M02Add.Parameters.Single().ToTestDisplayString()); + Assert.Empty(c3M02Add.ExplicitInterfaceImplementations); + Assert.True(c3M02Add.IsStatic); + Assert.False(c3M02Add.IsAbstract); + Assert.False(c3M02Add.IsVirtual); + Assert.False(c3M02Add.IsMetadataVirtual()); + Assert.False(c3M02Add.IsMetadataFinal); + Assert.False(c3M02Add.IsMetadataNewSlot()); + + Assert.Equal(MethodKind.EventRemove, c3M02Remove.MethodKind); + Assert.Equal("void C3.M02.remove", c3M02Remove.ToTestDisplayString()); + Assert.Equal("System.Void", c3M02Remove.ReturnTypeWithAnnotations.ToTestDisplayString()); + Assert.Empty(c3M02Remove.ExplicitInterfaceImplementations); + Assert.True(c3M02Remove.IsStatic); + Assert.False(c3M02Remove.IsAbstract); + Assert.False(c3M02Remove.IsVirtual); + Assert.False(c3M02Remove.IsMetadataVirtual()); + Assert.False(c3M02Remove.IsMetadataFinal); + Assert.False(c3M02Remove.IsMetadataNewSlot()); + + if (module is PEModuleSymbol) + { + c3M02Add = (MethodSymbol)c3.FindImplementationForInterfaceMember(m02.AddMethod); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c3M02Add.MethodKind); + Assert.Equal("void C3.I2.add_M02(System.Action modopt(I1) value)", c3M02Add.ToTestDisplayString()); + Assert.Same(m02.AddMethod, c3M02Add.ExplicitInterfaceImplementations.Single()); + + Assert.True(c3M02Add.IsStatic); + Assert.False(c3M02Add.IsAbstract); + Assert.False(c3M02Add.IsVirtual); + Assert.False(c3M02Add.IsMetadataVirtual()); + Assert.False(c3M02Add.IsMetadataFinal); + Assert.False(c3M02Add.IsMetadataNewSlot()); + + c3M02Remove = (MethodSymbol)c3.FindImplementationForInterfaceMember(m02.RemoveMethod); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c3M02Remove.MethodKind); + Assert.Equal("void modopt(I2) C3.I2.remove_M02(System.Action value)", c3M02Remove.ToTestDisplayString()); + Assert.Same(m02.RemoveMethod, c3M02Remove.ExplicitInterfaceImplementations.Single()); + + Assert.True(c3M02Remove.IsStatic); + Assert.False(c3M02Remove.IsAbstract); + Assert.False(c3M02Remove.IsVirtual); + Assert.False(c3M02Remove.IsMetadataVirtual()); + Assert.False(c3M02Remove.IsMetadataFinal); + Assert.False(c3M02Remove.IsMetadataNewSlot()); + + // Forwarding methods aren't tied to an event + Assert.Null(c3.FindImplementationForInterfaceMember(m02)); + } + else + { + Assert.Same(c3M02, c3.FindImplementationForInterfaceMember(m02)); + Assert.Same(c3M02Add, c3.FindImplementationForInterfaceMember(m02.AddMethod)); + Assert.Same(c3M02Remove, c3.FindImplementationForInterfaceMember(m02.RemoveMethod)); + } + + var c4 = module.GlobalNamespace.GetTypeMember("C4"); + + var c4M02 = (EventSymbol)c4.FindImplementationForInterfaceMember(m02); + var c4M02Add = c4M02.AddMethod; + var c4M02Remove = c4M02.RemoveMethod; + + Assert.Equal("event System.Action C4.I2.M02", c4M02.ToTestDisplayString()); + + // Signatures of accessors are lacking custom modifiers due to https://github.com/dotnet/roslyn/issues/53390. + + Assert.True(c4M02.IsStatic); + Assert.False(c4M02.IsAbstract); + Assert.False(c4M02.IsVirtual); + Assert.Same(m02, c4M02.ExplicitInterfaceImplementations.Single()); + + Assert.True(c4M02Add.IsStatic); + Assert.False(c4M02Add.IsAbstract); + Assert.False(c4M02Add.IsVirtual); + Assert.False(c4M02Add.IsMetadataVirtual()); + Assert.False(c4M02Add.IsMetadataFinal); + Assert.False(c4M02Add.IsMetadataNewSlot()); + Assert.Equal(MethodKind.EventAdd, c4M02Add.MethodKind); + Assert.Equal("void C4.I2.M02.add", c4M02Add.ToTestDisplayString()); + Assert.Equal("System.Action value", c4M02Add.Parameters.Single().ToTestDisplayString()); + Assert.Equal("System.Void", c4M02Add.ReturnTypeWithAnnotations.ToTestDisplayString()); + Assert.Same(m02.AddMethod, c4M02Add.ExplicitInterfaceImplementations.Single()); + Assert.Same(c4M02Add, c4.FindImplementationForInterfaceMember(m02.AddMethod)); + + Assert.True(c4M02Remove.IsStatic); + Assert.False(c4M02Remove.IsAbstract); + Assert.False(c4M02Remove.IsVirtual); + Assert.False(c4M02Remove.IsMetadataVirtual()); + Assert.False(c4M02Remove.IsMetadataFinal); + Assert.False(c4M02Remove.IsMetadataNewSlot()); + Assert.Equal(MethodKind.EventRemove, c4M02Remove.MethodKind); + Assert.Equal("void C4.I2.M02.remove", c4M02Remove.ToTestDisplayString()); + Assert.Equal("System.Action value", c4M02Remove.Parameters.Single().ToTestDisplayString()); + Assert.Equal("System.Void", c4M02Remove.ReturnTypeWithAnnotations.ToTestDisplayString()); + Assert.Same(m02.RemoveMethod, c4M02Remove.ExplicitInterfaceImplementations.Single()); + Assert.Same(c4M02Remove, c4.FindImplementationForInterfaceMember(m02.RemoveMethod)); + + Assert.Same(c4M02, c4.GetMembers().OfType().Single()); + Assert.Equal(2, c4.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); } verifier.VerifyIL("C1.I1.add_M01", @@ -20727,6 +21503,28 @@ .maxstack 1 IL_0001: call ""void C1.M01.remove"" IL_0006: ret } +"); + + verifier.VerifyIL("C3.I2.add_M02", +@" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""void C3.M02.add"" + IL_0006: ret +} +"); + + verifier.VerifyIL("C3.I2.remove_M02", +@" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""void C3.M02.remove"" + IL_0006: ret +} "); } From ba1ec643659a2b5a50a41c5ef068fafbbdc3ac93 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Fri, 14 May 2021 12:50:00 -0700 Subject: [PATCH 080/127] Correctness fixes --- .../Core/Portable/ValueTracking/ValueTrackingService.cs | 2 ++ .../Services/ValueTracking/RemoteValueTrackingService.cs | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs b/src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs index 091ac10716a21..1cdd3394a26b3 100644 --- a/src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs +++ b/src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ValueTracking { @@ -50,6 +51,7 @@ public async Task> TrackValueSourceAsync( foreach (var item in result.Value) { var rehydratedItem = await item.RehydrateAsync(document.Project.Solution, cancellationToken).ConfigureAwait(false); + Contract.ThrowIfNull(rehydratedItem); builder.Add(rehydratedItem); } } diff --git a/src/Workspaces/Remote/ServiceHub/Services/ValueTracking/RemoteValueTrackingService.cs b/src/Workspaces/Remote/ServiceHub/Services/ValueTracking/RemoteValueTrackingService.cs index 3ad451be7aab2..7f3f9e86ac4a6 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/ValueTracking/RemoteValueTrackingService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/ValueTracking/RemoteValueTrackingService.cs @@ -25,7 +25,6 @@ public RemoteValueTrackingService(ServiceConstructionArguments arguments) { } - public ValueTask> TrackValueSourceAsync(PinnedSolutionInfo solutionInfo, TextSpan selection, DocumentId documentId, CancellationToken cancellationToken) { return RunServiceAsync(async cancellationToken => From 9a4d609c2c29d7d3fb2ad4e247d113e923c3af8b Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Tue, 18 May 2021 12:39:23 -0700 Subject: [PATCH 081/127] Actually return builder stuff... --- .../Core/Portable/ValueTracking/ValueTrackingService.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs b/src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs index 1cdd3394a26b3..5b7414919cce7 100644 --- a/src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs +++ b/src/Features/Core/Portable/ValueTracking/ValueTrackingService.cs @@ -46,7 +46,7 @@ public async Task> TrackValueSourceAsync( return ImmutableArray.Empty; } - _ = PooledObjects.ArrayBuilder.GetInstance(out var builder); + using var _ = PooledObjects.ArrayBuilder.GetInstance(out var builder); foreach (var item in result.Value) { @@ -54,6 +54,8 @@ public async Task> TrackValueSourceAsync( Contract.ThrowIfNull(rehydratedItem); builder.Add(rehydratedItem); } + + return builder.ToImmutable(); } var progressTracker = new ValueTrackingProgressCollector(); @@ -82,7 +84,7 @@ public async Task> TrackValueSourceAsync( return ImmutableArray.Empty; } - _ = PooledObjects.ArrayBuilder.GetInstance(out var builder); + using var _ = PooledObjects.ArrayBuilder.GetInstance(out var builder); foreach (var item in result.Value) { @@ -94,6 +96,8 @@ public async Task> TrackValueSourceAsync( builder.Add(rehydratedItem); } + + return builder.ToImmutable(); } var progressTracker = new ValueTrackingProgressCollector(); From 384af1379836382d11059bdacab117a9c61e3515 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Tue, 18 May 2021 18:19:05 -0700 Subject: [PATCH 082/127] Enable declaration of conversion operators in interfaces. (#53434) --- .../CSharp/Portable/CSharpResources.resx | 5 +- .../CSharp/Portable/Errors/ErrorCode.cs | 1 + .../Source/SourceMemberContainerSymbol.cs | 5 +- .../SourceUserDefinedConversionSymbol.cs | 5 + .../SourceUserDefinedOperatorSymbolBase.cs | 28 +- .../Portable/xlf/CSharpResources.cs.xlf | 9 +- .../Portable/xlf/CSharpResources.de.xlf | 9 +- .../Portable/xlf/CSharpResources.es.xlf | 9 +- .../Portable/xlf/CSharpResources.fr.xlf | 9 +- .../Portable/xlf/CSharpResources.it.xlf | 9 +- .../Portable/xlf/CSharpResources.ja.xlf | 9 +- .../Portable/xlf/CSharpResources.ko.xlf | 9 +- .../Portable/xlf/CSharpResources.pl.xlf | 9 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 9 +- .../Portable/xlf/CSharpResources.ru.xlf | 9 +- .../Portable/xlf/CSharpResources.tr.xlf | 9 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 9 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 9 +- .../Test/Semantic/Semantics/OperatorTests.cs | 2 +- .../DefaultInterfaceImplementationTests.cs | 12 + .../StaticAbstractMembersInInterfacesTests.cs | 576 ++++++++++++++++-- .../Test/Symbol/Symbols/SymbolErrorTests.cs | 4 +- 22 files changed, 661 insertions(+), 94 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 273b6dcd15849..02add8601a334 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -1665,7 +1665,7 @@ If such a class is used as a base class and if the deriving class defines a dest '{0}': user-defined conversions to or from a derived type are not allowed - User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type + User-defined operator cannot convert a type to itself User-defined conversion must convert to or from the enclosing type @@ -6679,4 +6679,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Explicit implementation of a user-defined operator '{0}' must be declared static + + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index f34dcdcc6f624..ccc809cfc868c 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1953,6 +1953,7 @@ internal enum ErrorCode ERR_CloseUnimplementedInterfaceMemberNotStatic = 9109, ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember = 9110, ERR_ExplicitImplementationOfOperatorsMustBeStatic = 9111, + ERR_AbstractConversionNotInvolvingContainedType = 9112, // Note: you will need to re-generate compiler code after adding warnings (eng\generate-compiler-code.cmd) } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index 757410acaa573..f1ef1cbbbf131 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -3375,7 +3375,10 @@ private static void CheckInterfaceMember(Symbol member, BindingDiagnosticBag dia diagnostics.Add(ErrorCode.ERR_InterfacesCantContainConstructors, member.Locations[0]); break; case MethodKind.Conversion: - diagnostics.Add(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, member.Locations[0]); + if (!meth.IsAbstract) + { + diagnostics.Add(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, member.Locations[0]); + } break; case MethodKind.UserDefinedOperator: if (!meth.IsAbstract && (meth.Name == WellKnownMemberNames.EqualityOperatorName || meth.Name == WellKnownMemberNames.InequalityOperatorName)) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs index 2927f0d007121..26b1c417d7cae 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs @@ -61,6 +61,11 @@ private SourceUserDefinedConversionSymbol( { diagnostics.Add(ErrorCode.ERR_OvlUnaryOperatorExpected, syntax.ParameterList.GetLocation()); } + + if (IsStatic && IsAbstract) + { + CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasBody: syntax.Body != null || syntax.ExpressionBody != null, diagnostics: diagnostics); + } } internal ConversionOperatorDeclarationSyntax GetSyntax() diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs index fed9496646932..5929f7ddb6da5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs @@ -13,6 +13,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { internal abstract class SourceUserDefinedOperatorSymbolBase : SourceOrdinaryMethodOrUserDefinedOperatorSymbol { + // tomat: ignoreDynamic should be true, but we don't want to introduce breaking change. See bug 605326. private const TypeCompareKind ComparisonForUserDefinedOperators = TypeCompareKind.IgnoreTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes; private readonly string _name; private readonly bool _isExpressionBodied; @@ -48,7 +49,7 @@ protected SourceUserDefinedOperatorSymbolBase( this.MakeFlags(methodKind, declarationModifiers, returnsVoid: false, isExtensionMethod: false, isNullableAnalysisEnabled: isNullableAnalysisEnabled); if (this.ContainingType.IsInterface && - (methodKind == MethodKind.Conversion || (!IsAbstract && (name == WellKnownMemberNames.EqualityOperatorName || name == WellKnownMemberNames.InequalityOperatorName)))) + !IsAbstract && (methodKind == MethodKind.Conversion || name == WellKnownMemberNames.EqualityOperatorName || name == WellKnownMemberNames.InequalityOperatorName)) { // If we have an unsupported conversion or equality/inequality operator in an interface, we already have reported that fact as // an error. No need to cascade the error further. @@ -133,7 +134,7 @@ protected static DeclarationModifiers MakeDeclarationModifiers(MethodKind method { allowedModifiers |= DeclarationModifiers.Abstract; - if (syntax is OperatorDeclarationSyntax { OperatorToken: var opToken1 } && opToken1.Kind() is not (SyntaxKind.EqualsEqualsToken or SyntaxKind.ExclamationEqualsToken)) + if (syntax is OperatorDeclarationSyntax { OperatorToken: var opToken } && opToken.Kind() is not (SyntaxKind.EqualsEqualsToken or SyntaxKind.ExclamationEqualsToken)) { allowedModifiers |= DeclarationModifiers.Sealed; } @@ -143,7 +144,7 @@ protected static DeclarationModifiers MakeDeclarationModifiers(MethodKind method var result = ModifierUtils.MakeAndCheckNontypeMemberModifiers( syntax.Modifiers, defaultAccess, allowedModifiers, location, diagnostics, modifierErrors: out _); - if (inInterface && syntax is OperatorDeclarationSyntax { OperatorToken: var opToken2 }) + if (inInterface) { if ((result & (DeclarationModifiers.Abstract | DeclarationModifiers.Sealed)) != 0) { @@ -167,7 +168,7 @@ protected static DeclarationModifiers MakeDeclarationModifiers(MethodKind method result &= ~DeclarationModifiers.Sealed; } - else if ((result & DeclarationModifiers.Static) != 0 && opToken2.Kind() is not (SyntaxKind.EqualsEqualsToken or SyntaxKind.ExclamationEqualsToken)) + else if ((result & DeclarationModifiers.Static) != 0 && syntax is OperatorDeclarationSyntax { OperatorToken: var opToken } && opToken.Kind() is not (SyntaxKind.EqualsEqualsToken or SyntaxKind.ExclamationEqualsToken)) { Binder.CheckFeatureAvailability(location.SourceTree, MessageID.IDS_DefaultInterfaceImplementation, diagnostics, location); } @@ -247,10 +248,9 @@ protected override void MethodChecks(BindingDiagnosticBag diagnostics) MethodChecks(returnType, parameters, diagnostics); - // If we have a conversion operator in an interface or static class then we already + // If we have a static class then we already // have reported that fact as an error. No need to cascade the error further. - if ((this.ContainingType.IsInterfaceType() && MethodKind == MethodKind.Conversion) || - this.ContainingType.IsStatic) + if (this.ContainingType.IsStatic) { return; } @@ -403,7 +403,7 @@ private void CheckUserDefinedConversionSignature(BindingDiagnosticBag diagnostic !MatchesContainingType(target)) { // CS0556: User-defined conversion must convert to or from the enclosing type - diagnostics.Add(ErrorCode.ERR_ConversionNotInvolvingContainedType, this.Locations[0]); + diagnostics.Add(IsAbstract ? ErrorCode.ERR_AbstractConversionNotInvolvingContainedType : ErrorCode.ERR_ConversionNotInvolvingContainedType, this.Locations[0]); return; } @@ -413,8 +413,7 @@ private void CheckUserDefinedConversionSignature(BindingDiagnosticBag diagnostic ? source.Equals(target, ComparisonForUserDefinedOperators) : source0.Equals(target0, ComparisonForUserDefinedOperators)) { - // CS0555: User-defined operator cannot take an object of the enclosing type - // and convert to an object of the enclosing type + // CS0555: User-defined operator cannot convert a type to itself diagnostics.Add(ErrorCode.ERR_IdentityConversion, this.Locations[0]); return; } @@ -501,22 +500,19 @@ private void CheckUserDefinedConversionSignature(BindingDiagnosticBag diagnostic different = source; } - if (different.IsClassType()) + if (different.IsClassType() && !same.IsTypeParameter()) { // different is a class type: Debug.Assert(!different.IsTypeParameter()); - // "same" is the containing class, so it can't be a type parameter - Debug.Assert(!same.IsTypeParameter()); - var useSiteInfo = new CompoundUseSiteInfo(diagnostics, ContainingAssembly); - if (same.IsDerivedFrom(different, ComparisonForUserDefinedOperators, useSiteInfo: ref useSiteInfo)) // tomat: ignoreDynamic should be true, but we don't want to introduce breaking change. See bug 605326. + if (same.IsDerivedFrom(different, ComparisonForUserDefinedOperators, useSiteInfo: ref useSiteInfo)) { // '{0}': user-defined conversions to or from a base type are not allowed diagnostics.Add(ErrorCode.ERR_ConversionWithBase, this.Locations[0], this); } - else if (different.IsDerivedFrom(same, ComparisonForUserDefinedOperators, useSiteInfo: ref useSiteInfo)) // tomat: ignoreDynamic should be true, but we don't want to introduce breaking change. See bug 605326. + else if (different.IsDerivedFrom(same, ComparisonForUserDefinedOperators, useSiteInfo: ref useSiteInfo)) { // '{0}': user-defined conversions to or from a derived type are not allowed diagnostics.Add(ErrorCode.ERR_ConversionWithDerived, this.Locations[0], this); diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 0a2346499971a..3ea58f2cb36c3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -22,6 +22,11 @@ Konstruovaný obecný typ nejde vytvořit z jiného než obecného typu. + + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + + '{0}': abstract event cannot use event accessor syntax {0}: abstraktní událost nemůže používat syntaxi přístupového objektu události. @@ -5126,8 +5131,8 @@ Pokud se taková třída používá jako základní třída a pokud odvozující - User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type - Uživatelem definovaný operátor nemůže převzít objekt nadřazeného typu a převést jej na objekt nadřazeného typu. + User-defined operator cannot convert a type to itself + Uživatelem definovaný operátor nemůže převzít objekt nadřazeného typu a převést jej na objekt nadřazeného typu. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 220e6777f859f..baa6ce15bc573 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -22,6 +22,11 @@ Es kann kein konstruierter generischer Typ aus einem nicht generischen Typ erstellt werden. + + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + + '{0}': abstract event cannot use event accessor syntax {0}: Das abstrakte Ereignis kann die Ereignisaccessorsyntax nicht verwenden. @@ -5126,8 +5131,8 @@ Wenn solch eine Klasse als Basisklasse verwendet wird und die ableitende Klasse - User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type - Ein benutzerdefinierter Operator kann kein Objekt vom einschließenden Typ übernehmen oder in ein Objekt des einschließenden Typs konvertieren. + User-defined operator cannot convert a type to itself + Ein benutzerdefinierter Operator kann kein Objekt vom einschließenden Typ übernehmen oder in ein Objekt des einschließenden Typs konvertieren. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 17f8eb9bf55b1..d32e66bc7375e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -22,6 +22,11 @@ No se puede crear un tipo genérico construido a partir de un tipo no genérico. + + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + + '{0}': abstract event cannot use event accessor syntax "{0}": un evento abstracto no puede usar la sintaxis de descriptor de acceso de eventos @@ -5126,8 +5131,8 @@ Si se utiliza una clase de este tipo como clase base y si la clase derivada defi - User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type - Un operador definido por el usuario no puede adquirir un objeto de tipo envolvente ni convertirlo en un objeto de tipo envolvente + User-defined operator cannot convert a type to itself + Un operador definido por el usuario no puede adquirir un objeto de tipo envolvente ni convertirlo en un objeto de tipo envolvente diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 9f6e436e91291..f073d0d0818cd 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -22,6 +22,11 @@ Impossible de créer un type générique construit à partir d'un type non générique. + + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + + '{0}': abstract event cannot use event accessor syntax '{0}' : un événement abstrait ne peut pas utiliser une syntaxe d'accesseur d'événement @@ -5126,8 +5131,8 @@ Si une telle classe est utilisée en tant que classe de base et si la classe dé - User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type - L'opérateur défini par l'utilisateur ne peut pas prendre un objet du type englobant et le convertir en un objet du type englobant + User-defined operator cannot convert a type to itself + L'opérateur défini par l'utilisateur ne peut pas prendre un objet du type englobant et le convertir en un objet du type englobant diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 274d4003b19b9..33c5e1b47ff2a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -22,6 +22,11 @@ Non è possibile creare un tipo generico costruito a partire da un tipo non generico. + + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + + '{0}': abstract event cannot use event accessor syntax '{0}': l'evento astratto non può usare la sintassi della funzione di accesso agli eventi @@ -5126,8 +5131,8 @@ Se si usa tale classe come classe base e se la classe di derivazione definisce u - User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type - L'operatore definito dall'utente non può accettare un oggetto del tipo di inclusione e convertirlo in un oggetto del tipo di inclusione + User-defined operator cannot convert a type to itself + L'operatore definito dall'utente non può accettare un oggetto del tipo di inclusione e convertirlo in un oggetto del tipo di inclusione diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index aa37445063193..51208f000dced 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -22,6 +22,11 @@ 非ジェネリック型から、構築済みジェネリック型を作成できません。 + + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + + '{0}': abstract event cannot use event accessor syntax '{0}': 抽象イベントはイベント アクセサーの構文を使用できません @@ -5126,8 +5131,8 @@ If such a class is used as a base class and if the deriving class defines a dest - User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type - ユーザー定義の演算子は、それを囲む型のオブジェクトの取得、およびそれを囲む型のオブジェクトへの変換を行えません + User-defined operator cannot convert a type to itself + ユーザー定義の演算子は、それを囲む型のオブジェクトの取得、およびそれを囲む型のオブジェクトへの変換を行えません diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index f63b2217d746d..4308b96fcb749 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -22,6 +22,11 @@ 제네릭이 아닌 형식에서 생성된 제네릭 형식을 만들 수 없습니다. + + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + + '{0}': abstract event cannot use event accessor syntax '{0}': 추상 이벤트는 이벤트 접근자 구문을 사용할 수 없습니다. @@ -5125,8 +5130,8 @@ If such a class is used as a base class and if the deriving class defines a dest - User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type - 사용자 정의 연산자는 바깥쪽 형식의 개체를 가져와서 바깥쪽 형식의 개체로 변환할 수 없습니다. + User-defined operator cannot convert a type to itself + 사용자 정의 연산자는 바깥쪽 형식의 개체를 가져와서 바깥쪽 형식의 개체로 변환할 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 4c116038ff0ee..96d1b05da4775 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -22,6 +22,11 @@ Nie można utworzyć konstruowanego typu ogólnego z typu nieogólnego. + + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + + '{0}': abstract event cannot use event accessor syntax „{0}”: zdarzenie abstrakcyjne nie może używać składni metody dostępu zdarzenia @@ -5126,8 +5131,8 @@ Jeśli taka klasa zostanie użyta jako klasa bazowa i klasa pochodna definiuje d - User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type - Zdefiniowany przez użytkownika operator nie może pobrać obiektu typu otaczającego i dokonać konwersji na obiekt typu otaczającego + User-defined operator cannot convert a type to itself + Zdefiniowany przez użytkownika operator nie może pobrać obiektu typu otaczającego i dokonać konwersji na obiekt typu otaczającego diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index cfc153ff69dfd..3de0df26cc89c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -22,6 +22,11 @@ Não é possível criar um tipo genérico construído com base em um tipo não genérico. + + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + + '{0}': abstract event cannot use event accessor syntax '{0}': o evento abstrato não pode usar a sintaxe do acessador de eventos @@ -5126,8 +5131,8 @@ Se tal classe for usada como uma classe base e se a classe derivada definir um d - User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type - O operador definido pelo usuário não pode obter um objeto do tipo delimitador e se converter em um objeto do tipo delimitador + User-defined operator cannot convert a type to itself + O operador definido pelo usuário não pode obter um objeto do tipo delimitador e se converter em um objeto do tipo delimitador diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index fe91f8188e175..4b0eaaf0f5a47 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -22,6 +22,11 @@ Не удается создать сконструированный универсальный тип из неуниверсального типа. + + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + + '{0}': abstract event cannot use event accessor syntax "{0}": абстрактное событие не может использовать синтаксис метода доступа к событиям. @@ -5126,8 +5131,8 @@ If such a class is used as a base class and if the deriving class defines a dest - User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type - Определенный пользователем оператор не может получить объект данного включающего типа и выполнить преобразование в объект данного включающего типа. + User-defined operator cannot convert a type to itself + Определенный пользователем оператор не может получить объект данного включающего типа и выполнить преобразование в объект данного включающего типа. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index f69fb0b01f293..6f25c40717652 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -22,6 +22,11 @@ Oluşturulmuş genel tür, genel olmayan türden oluşturulamaz. + + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + + '{0}': abstract event cannot use event accessor syntax '{0}': soyut olay, olay erişeni söz dizimini kullanamaz @@ -5126,8 +5131,8 @@ Bu sınıf temel sınıf olarak kullanılırsa ve türetilen sınıf bir yıkıc - User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type - Kullanıcı tanımlı işleç kapsayan türdeki bir nesneyi alamaz ve kapsayan türdeki bir nesneye dönüştüremez + User-defined operator cannot convert a type to itself + Kullanıcı tanımlı işleç kapsayan türdeki bir nesneyi alamaz ve kapsayan türdeki bir nesneye dönüştüremez diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 9f8a869bb52a6..85b4361c5209d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -22,6 +22,11 @@ 无法从非泛型类型创建构造泛型类型。 + + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + + '{0}': abstract event cannot use event accessor syntax “{0}”: 抽象事件不可使用事件访问器语法 @@ -5131,8 +5136,8 @@ If such a class is used as a base class and if the deriving class defines a dest - User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type - 用户定义的运算符不能采用封闭类型的对象,也不能转换成封闭类型的对象 + User-defined operator cannot convert a type to itself + 用户定义的运算符不能采用封闭类型的对象,也不能转换成封闭类型的对象 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index c2b085d4f741d..2d8f3e802f49d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -22,6 +22,11 @@ 無法從另一個非泛型型別建立建構的泛型型別。 + + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + + '{0}': abstract event cannot use event accessor syntax '{0}' 抽象事件無法使用事件存取子語法 @@ -5126,8 +5131,8 @@ If such a class is used as a base class and if the deriving class defines a dest - User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type - 使用者定義的運算子無法攜帶封入類型的物件和轉換為封入類型的物件 + User-defined operator cannot convert a type to itself + 使用者定義的運算子無法攜帶封入類型的物件和轉換為封入類型的物件 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs index f077ab1a231f4..44d5fc2dd4851 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs @@ -6051,7 +6051,7 @@ public static explicit operator (T1 fst, T2 snd)((T1 one, T2 two) s) } "; CreateCompilation(text).VerifyDiagnostics( - // (6,41): error CS0555: User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type + // (6,41): error CS0555: User-defined operator cannot convert a type to itself // public static explicit operator (T1 fst, T2 snd)((T1 one, T2 two) s) Diagnostic(ErrorCode.ERR_IdentityConversion, "(T1 fst, T2 snd)").WithLocation(6, 41)); } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs index e652a27b09348..de8c324c6e1b2 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs @@ -41301,9 +41301,15 @@ public static void Test(I1 x) var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, targetFramework: TargetFramework.NetCoreApp, parseOptions: TestOptions.Regular); compilation1.VerifyDiagnostics( + // (4,37): error CS0552: 'I1.implicit operator int(I1)': user-defined conversions to or from an interface are not allowed + // public static implicit operator int(I1 x) + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "int").WithArguments("I1.implicit operator int(I1)").WithLocation(4, 37), // (4,37): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract // public static implicit operator int(I1 x) Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "int").WithLocation(4, 37), + // (8,37): error CS0552: 'I1.explicit operator byte(I1)': user-defined conversions to or from an interface are not allowed + // public static explicit operator byte(I1 x) + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "byte").WithArguments("I1.explicit operator byte(I1)").WithLocation(8, 37), // (8,37): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract // public static explicit operator byte(I1 x) Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "byte").WithLocation(8, 37), @@ -43717,9 +43723,15 @@ static explicit operator byte(I1 x) } compilation1.VerifyDiagnostics( + // (4,30): error CS0552: 'I1.implicit operator int(I1)': user-defined conversions to or from an interface are not allowed + // static implicit operator int(I1 x) + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "int").WithArguments("I1.implicit operator int(I1)").WithLocation(4, 30), // (4,30): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract // static implicit operator int(I1 x) Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "int").WithLocation(4, 30), + // (9,30): error CS0552: 'I1.explicit operator byte(I1)': user-defined conversions to or from an interface are not allowed + // static explicit operator byte(I1 x) + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "byte").WithArguments("I1.explicit operator byte(I1)").WithLocation(9, 30), // (9,30): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract // static explicit operator byte(I1 x) Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "byte").WithLocation(9, 30) diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index 2c79f5877a5bb..f6e7e704aa139 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -3987,10 +3987,8 @@ void validate() } } - [Theory] - [InlineData(false)] - [InlineData(true)] - public void OperatorModifiers_08(bool use7_3) + [Fact] + public void OperatorModifiers_08() { var source1 = @" @@ -3998,40 +3996,241 @@ public interface I1 { abstract static implicit operator int(I1 x); - abstract static explicit operator bool(I1 x) {return false;} + abstract static explicit operator I1(bool x) {return null;} } public interface I2 { - sealed static implicit operator int(I2 x) {return 0;} + sealed static implicit operator int(I2 x); + + sealed static explicit operator I2(bool x) {return null;} +} + +public interface I3 +{ + abstract sealed static implicit operator int(I3 x); - sealed static explicit operator bool(I2 x) {return false;} + abstract sealed static explicit operator I3(bool x) {return null;} } "; var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, - parseOptions: use7_3 ? TestOptions.Regular7_3 : TestOptions.RegularPreview, + parseOptions: TestOptions.Regular7_3, targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (4,39): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // (4,39): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract static implicit operator int(I1 x); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "int").WithArguments("abstract", "7.3", "preview").WithLocation(4, 39), + // (4,39): error CS0552: 'I1.implicit operator int(I1)': user-defined conversions to or from an interface are not allowed + // abstract static implicit operator int(I1 x); + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "int").WithArguments("I1.implicit operator int(I1)").WithLocation(4, 39), + // (6,39): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract static explicit operator I1(bool x) {return null;} + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "I1").WithArguments("abstract", "7.3", "preview").WithLocation(6, 39), + // (6,39): error CS0500: 'I1.explicit operator I1(bool)' cannot declare a body because it is marked abstract + // abstract static explicit operator I1(bool x) {return null;} + Diagnostic(ErrorCode.ERR_AbstractHasBody, "I1").WithArguments("I1.explicit operator I1(bool)").WithLocation(6, 39), + // (6,39): error CS0552: 'I1.explicit operator I1(bool)': user-defined conversions to or from an interface are not allowed + // abstract static explicit operator I1(bool x) {return null;} + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "I1").WithArguments("I1.explicit operator I1(bool)").WithLocation(6, 39), + // (11,37): error CS0106: The modifier 'sealed' is not valid for this item + // sealed static implicit operator int(I2 x); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("sealed").WithLocation(11, 37), + // (11,37): error CS0552: 'I2.implicit operator int(I2)': user-defined conversions to or from an interface are not allowed + // sealed static implicit operator int(I2 x); + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "int").WithArguments("I2.implicit operator int(I2)").WithLocation(11, 37), + // (11,37): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // sealed static implicit operator int(I2 x); + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "int").WithLocation(11, 37), + // (13,37): error CS0106: The modifier 'sealed' is not valid for this item + // sealed static explicit operator I2(bool x) {return null;} + Diagnostic(ErrorCode.ERR_BadMemberFlag, "I2").WithArguments("sealed").WithLocation(13, 37), + // (13,37): error CS0552: 'I2.explicit operator I2(bool)': user-defined conversions to or from an interface are not allowed + // sealed static explicit operator I2(bool x) {return null;} + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "I2").WithArguments("I2.explicit operator I2(bool)").WithLocation(13, 37), + // (13,37): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // sealed static explicit operator I2(bool x) {return null;} + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "I2").WithLocation(13, 37), + // (18,46): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static implicit operator int(I3 x); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("sealed").WithLocation(18, 46), + // (18,46): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract sealed static implicit operator int(I3 x); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "int").WithArguments("abstract", "7.3", "preview").WithLocation(18, 46), + // (18,46): error CS0552: 'I3.implicit operator int(I3)': user-defined conversions to or from an interface are not allowed + // abstract sealed static implicit operator int(I3 x); + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "int").WithArguments("I3.implicit operator int(I3)").WithLocation(18, 46), + // (20,46): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static explicit operator I3(bool x) {return null;} + Diagnostic(ErrorCode.ERR_BadMemberFlag, "I3").WithArguments("sealed").WithLocation(20, 46), + // (20,46): error CS8703: The modifier 'abstract' is not valid for this item in C# 7.3. Please use language version 'preview' or greater. + // abstract sealed static explicit operator I3(bool x) {return null;} + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "I3").WithArguments("abstract", "7.3", "preview").WithLocation(20, 46), + // (20,46): error CS0500: 'I3.explicit operator I3(bool)' cannot declare a body because it is marked abstract + // abstract sealed static explicit operator I3(bool x) {return null;} + Diagnostic(ErrorCode.ERR_AbstractHasBody, "I3").WithArguments("I3.explicit operator I3(bool)").WithLocation(20, 46), + // (20,46): error CS0552: 'I3.explicit operator I3(bool)': user-defined conversions to or from an interface are not allowed + // abstract sealed static explicit operator I3(bool x) {return null;} + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "I3").WithArguments("I3.explicit operator I3(bool)").WithLocation(20, 46) + ); + + validate(); + + compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,39): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static implicit operator int(I1 x); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "int").WithArguments("abstract", "9.0", "preview").WithLocation(4, 39), + // (4,39): error CS0552: 'I1.implicit operator int(I1)': user-defined conversions to or from an interface are not allowed + // abstract static implicit operator int(I1 x); + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "int").WithArguments("I1.implicit operator int(I1)").WithLocation(4, 39), + // (6,39): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static explicit operator I1(bool x) {return null;} + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "I1").WithArguments("abstract", "9.0", "preview").WithLocation(6, 39), + // (6,39): error CS0500: 'I1.explicit operator I1(bool)' cannot declare a body because it is marked abstract + // abstract static explicit operator I1(bool x) {return null;} + Diagnostic(ErrorCode.ERR_AbstractHasBody, "I1").WithArguments("I1.explicit operator I1(bool)").WithLocation(6, 39), + // (6,39): error CS0552: 'I1.explicit operator I1(bool)': user-defined conversions to or from an interface are not allowed + // abstract static explicit operator I1(bool x) {return null;} + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "I1").WithArguments("I1.explicit operator I1(bool)").WithLocation(6, 39), + // (11,37): error CS0106: The modifier 'sealed' is not valid for this item + // sealed static implicit operator int(I2 x); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("sealed").WithLocation(11, 37), + // (11,37): error CS0552: 'I2.implicit operator int(I2)': user-defined conversions to or from an interface are not allowed + // sealed static implicit operator int(I2 x); + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "int").WithArguments("I2.implicit operator int(I2)").WithLocation(11, 37), + // (11,37): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // sealed static implicit operator int(I2 x); + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "int").WithLocation(11, 37), + // (13,37): error CS0106: The modifier 'sealed' is not valid for this item + // sealed static explicit operator I2(bool x) {return null;} + Diagnostic(ErrorCode.ERR_BadMemberFlag, "I2").WithArguments("sealed").WithLocation(13, 37), + // (13,37): error CS0552: 'I2.explicit operator I2(bool)': user-defined conversions to or from an interface are not allowed + // sealed static explicit operator I2(bool x) {return null;} + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "I2").WithArguments("I2.explicit operator I2(bool)").WithLocation(13, 37), + // (13,37): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // sealed static explicit operator I2(bool x) {return null;} + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "I2").WithLocation(13, 37), + // (18,46): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static implicit operator int(I3 x); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("sealed").WithLocation(18, 46), + // (18,46): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract sealed static implicit operator int(I3 x); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "int").WithArguments("abstract", "9.0", "preview").WithLocation(18, 46), + // (18,46): error CS0552: 'I3.implicit operator int(I3)': user-defined conversions to or from an interface are not allowed + // abstract sealed static implicit operator int(I3 x); + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "int").WithArguments("I3.implicit operator int(I3)").WithLocation(18, 46), + // (20,46): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static explicit operator I3(bool x) {return null;} + Diagnostic(ErrorCode.ERR_BadMemberFlag, "I3").WithArguments("sealed").WithLocation(20, 46), + // (20,46): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract sealed static explicit operator I3(bool x) {return null;} + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "I3").WithArguments("abstract", "9.0", "preview").WithLocation(20, 46), + // (20,46): error CS0500: 'I3.explicit operator I3(bool)' cannot declare a body because it is marked abstract + // abstract sealed static explicit operator I3(bool x) {return null;} + Diagnostic(ErrorCode.ERR_AbstractHasBody, "I3").WithArguments("I3.explicit operator I3(bool)").WithLocation(20, 46), + // (20,46): error CS0552: 'I3.explicit operator I3(bool)': user-defined conversions to or from an interface are not allowed + // abstract sealed static explicit operator I3(bool x) {return null;} + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "I3").WithArguments("I3.explicit operator I3(bool)").WithLocation(20, 46) + ); + + validate(); + + compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,39): error CS0552: 'I1.implicit operator int(I1)': user-defined conversions to or from an interface are not allowed // abstract static implicit operator int(I1 x); - Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "int").WithLocation(4, 39), - // (6,39): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract - // abstract static explicit operator bool(I1 x) {return false;} - Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "bool").WithLocation(6, 39), + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "int").WithArguments("I1.implicit operator int(I1)").WithLocation(4, 39), + // (6,39): error CS0500: 'I1.explicit operator I1(bool)' cannot declare a body because it is marked abstract + // abstract static explicit operator I1(bool x) {return null;} + Diagnostic(ErrorCode.ERR_AbstractHasBody, "I1").WithArguments("I1.explicit operator I1(bool)").WithLocation(6, 39), + // (6,39): error CS0552: 'I1.explicit operator I1(bool)': user-defined conversions to or from an interface are not allowed + // abstract static explicit operator I1(bool x) {return null;} + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "I1").WithArguments("I1.explicit operator I1(bool)").WithLocation(6, 39), // (11,37): error CS0106: The modifier 'sealed' is not valid for this item - // sealed static implicit operator int(I2 x) {return 0;} + // sealed static implicit operator int(I2 x); Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("sealed").WithLocation(11, 37), + // (11,37): error CS0552: 'I2.implicit operator int(I2)': user-defined conversions to or from an interface are not allowed + // sealed static implicit operator int(I2 x); + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "int").WithArguments("I2.implicit operator int(I2)").WithLocation(11, 37), // (11,37): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract - // sealed static implicit operator int(I2 x) {return 0;} + // sealed static implicit operator int(I2 x); Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "int").WithLocation(11, 37), // (13,37): error CS0106: The modifier 'sealed' is not valid for this item - // sealed static explicit operator bool(I2 x) {return false;} - Diagnostic(ErrorCode.ERR_BadMemberFlag, "bool").WithArguments("sealed").WithLocation(13, 37), + // sealed static explicit operator I2(bool x) {return null;} + Diagnostic(ErrorCode.ERR_BadMemberFlag, "I2").WithArguments("sealed").WithLocation(13, 37), + // (13,37): error CS0552: 'I2.explicit operator I2(bool)': user-defined conversions to or from an interface are not allowed + // sealed static explicit operator I2(bool x) {return null;} + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "I2").WithArguments("I2.explicit operator I2(bool)").WithLocation(13, 37), // (13,37): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract - // sealed static explicit operator bool(I2 x) {return false;} - Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "bool").WithLocation(13, 37) + // sealed static explicit operator I2(bool x) {return null;} + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "I2").WithLocation(13, 37), + // (18,46): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static implicit operator int(I3 x); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("sealed").WithLocation(18, 46), + // (18,46): error CS0552: 'I3.implicit operator int(I3)': user-defined conversions to or from an interface are not allowed + // abstract sealed static implicit operator int(I3 x); + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "int").WithArguments("I3.implicit operator int(I3)").WithLocation(18, 46), + // (20,46): error CS0106: The modifier 'sealed' is not valid for this item + // abstract sealed static explicit operator I3(bool x) {return null;} + Diagnostic(ErrorCode.ERR_BadMemberFlag, "I3").WithArguments("sealed").WithLocation(20, 46), + // (20,46): error CS0500: 'I3.explicit operator I3(bool)' cannot declare a body because it is marked abstract + // abstract sealed static explicit operator I3(bool x) {return null;} + Diagnostic(ErrorCode.ERR_AbstractHasBody, "I3").WithArguments("I3.explicit operator I3(bool)").WithLocation(20, 46), + // (20,46): error CS0552: 'I3.explicit operator I3(bool)': user-defined conversions to or from an interface are not allowed + // abstract sealed static explicit operator I3(bool x) {return null;} + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "I3").WithArguments("I3.explicit operator I3(bool)").WithLocation(20, 46) ); + + validate(); + + void validate() + { + foreach (MethodSymbol m01 in compilation1.GetTypeByMetadataName("I1").GetMembers()) + { + Assert.True(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.True(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(m01.ContainingType.FindImplementationForInterfaceMember(m01)); + } + + foreach (MethodSymbol m01 in compilation1.GetTypeByMetadataName("I2").GetMembers()) + { + Assert.False(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.False(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(m01.ContainingType.FindImplementationForInterfaceMember(m01)); + } + + foreach (MethodSymbol m01 in compilation1.GetTypeByMetadataName("I3").GetMembers()) + { + Assert.True(m01.IsAbstract); + Assert.False(m01.IsVirtual); + Assert.True(m01.IsMetadataVirtual()); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsExtern); + Assert.False(m01.IsAsync); + Assert.False(m01.IsOverride); + Assert.Null(m01.ContainingType.FindImplementationForInterfaceMember(m01)); + } + } } [Fact] @@ -4531,6 +4730,69 @@ interface I1 ); } + [Fact] + public void DefineAbstractStaticConversion_01() + { + var source1 = +@" +interface I1 where T : I1 +{ + abstract static implicit operator int(T x); + abstract static explicit operator T(int x); +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + int count = 0; + foreach (var m01 in module.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType()) + { + Assert.False(m01.IsMetadataNewSlot()); + Assert.True(m01.IsAbstract); + Assert.True(m01.IsMetadataVirtual()); + Assert.False(m01.IsMetadataFinal); + Assert.False(m01.IsVirtual); + Assert.False(m01.IsSealed); + Assert.True(m01.IsStatic); + Assert.False(m01.IsOverride); + + count++; + } + + Assert.Equal(2, count); + } + } + + [Fact] + public void DefineAbstractStaticConversion_03() + { + var source1 = +@" +interface I1 where T : I1 +{ + abstract static implicit operator int(T x); + abstract static explicit operator T(int x); +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation1.VerifyDiagnostics( + // (4,39): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static implicit operator int(T x); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "int").WithLocation(4, 39), + // (5,39): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static explicit operator T(int x); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "T").WithLocation(5, 39) + ); + } + [Fact] public void DefineAbstractStaticProperty_01() { @@ -5320,19 +5582,8 @@ interface I15 where T151 : I15 where T152 : I15")] - [InlineData("<=")] - [InlineData(">=")] - public void OperatorSignature_04(string op) + [CombinatorialData] + public void OperatorSignature_04([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=", "==", "!=")] string op) { var source1 = @" @@ -5397,7 +5648,7 @@ interface I13 var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, parseOptions: TestOptions.RegularPreview, targetFramework: TargetFramework.NetCoreApp); - compilation1.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + compilation1.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_OperatorNeedsMatch or (int)ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators)).Verify( // (4,26): error CS0563: One of the parameters of a binary operator must be the containing type // static bool operator +(T1 x, bool y) => throw null; Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, op).WithLocation(4, 26), @@ -5426,19 +5677,8 @@ interface I13 } [Theory] - [InlineData("+")] - [InlineData("-")] - [InlineData("*")] - [InlineData("/")] - [InlineData("%")] - [InlineData("&")] - [InlineData("|")] - [InlineData("^")] - [InlineData("<")] - [InlineData(">")] - [InlineData("<=")] - [InlineData(">=")] - public void OperatorSignature_05(string op) + [CombinatorialData] + public void OperatorSignature_05([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "<=", ">=", "==", "!=")] string op) { var source1 = @" @@ -5503,7 +5743,7 @@ interface I13 var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, parseOptions: TestOptions.RegularPreview, targetFramework: TargetFramework.NetCoreApp); - compilation1.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + compilation1.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_OperatorNeedsMatch or (int)ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators)).Verify( // (4,26): error CS0563: One of the parameters of a binary operator must be the containing type // static bool operator +(bool y, T1 x) => throw null; Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, op).WithLocation(4, 26), @@ -5635,6 +5875,248 @@ interface I14 ); } + [Theory] + [CombinatorialData] + public void OperatorSignature_07([CombinatorialValues("implicit", "explicit")] string op) + { + var source1 = +@" +interface I1 where T1 : I1 +{ + abstract static " + op + @" operator T1(T1 y); +} + +interface I2 where T2 : I2 +{ + abstract static " + op + @" operator dynamic(T2 y); +} + +interface I3 where T3 : I3 +{ + static abstract " + op + @" operator T3(bool y); +} + +interface I4 where T4 : struct, I4 +{ + static abstract " + op + @" operator T4?(bool y); +} + +class C5 where T5 : C5.I6 +{ + public interface I6 + { + static abstract " + op + @" operator T5 (bool y); + } +} + +interface I7 where T72 : I7 where T71 : T72 +{ + static abstract " + op + @" operator T71 (bool y); +} + +interface I8 where T8 : I9 +{ + static abstract " + op + @" operator T8(bool y); +} + +interface I9 : I8 where T9 : I9 {} + +interface I10 where T10 : C11 +{ + static abstract " + op + @" operator T10(bool y); +} + +class C11 : I10 where T11 : C11 {} + +interface I12 +{ + static abstract " + op + @" operator int(bool y); +} + +interface I13 +{ + static abstract " + op + @" operator I13(bool y); +} + +interface I14 where T14 : I14 +{ + abstract static " + op + @" operator object(T14 y); +} + +class C15 {} +class C16 : C15 {} + +interface I17 where T17 : C15, I17 +{ + abstract static " + op + @" operator C16(T17 y); +} + +interface I18 where T18 : C16, I18 +{ + abstract static " + op + @" operator C15(T18 y); +} + +interface I19 where T19_1 : I19, T19_2 +{ + abstract static " + op + @" operator T19_1(T19_2 y); +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + compilation1.VerifyDiagnostics( + // (4,39): error CS0555: User-defined operator cannot convert a type to itself + // abstract static explicit operator T1(T1 y); + Diagnostic(ErrorCode.ERR_IdentityConversion, "T1").WithLocation(4, 39), + // (9,39): error CS1964: 'I2.explicit operator dynamic(T2)': user-defined conversions to or from the dynamic type are not allowed + // abstract static explicit operator dynamic(T2 y); + Diagnostic(ErrorCode.ERR_BadDynamicConversion, "dynamic").WithArguments("I2." + op + " operator dynamic(T2)").WithLocation(9, 39), + // (26,43): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // static abstract explicit operator T5 (bool y); + Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "T5").WithLocation(26, 43), + // (32,39): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // static abstract explicit operator T71 (bool y); + Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "T71").WithLocation(32, 39), + // (37,39): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // static abstract explicit operator T8(bool y); + Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "T8").WithLocation(37, 39), + // (44,39): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // static abstract explicit operator T10(bool y); + Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "T10").WithLocation(44, 39), + // (47,18): error CS0535: 'C11' does not implement interface member 'I10.explicit operator T11(bool)' + // class C11 : I10 where T11 : C11 {} + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I10").WithArguments("C11", "I10." + op + " operator T11(bool)").WithLocation(47, 18), + // (51,39): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // static abstract explicit operator int(bool y); + Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "int").WithLocation(51, 39), + // (56,39): error CS0552: 'I13.explicit operator I13(bool)': user-defined conversions to or from an interface are not allowed + // static abstract explicit operator I13(bool y); + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "I13").WithArguments("I13." + op + " operator I13(bool)").WithLocation(56, 39) + ); + } + + [Theory] + [CombinatorialData] + public void OperatorSignature_08([CombinatorialValues("implicit", "explicit")] string op) + { + var source1 = +@" +interface I1 where T1 : I1 +{ + abstract static " + op + @" operator T1(T1 y); +} + +interface I2 where T2 : I2 +{ + abstract static " + op + @" operator T2(dynamic y); +} + +interface I3 where T3 : I3 +{ + static abstract " + op + @" operator bool(T3 y); +} + +interface I4 where T4 : struct, I4 +{ + static abstract " + op + @" operator bool(T4? y); +} + +class C5 where T5 : C5.I6 +{ + public interface I6 + { + static abstract " + op + @" operator bool(T5 y); + } +} + +interface I7 where T72 : I7 where T71 : T72 +{ + static abstract " + op + @" operator bool(T71 y); +} + +interface I8 where T8 : I9 +{ + static abstract " + op + @" operator bool(T8 y); +} + +interface I9 : I8 where T9 : I9 {} + +interface I10 where T10 : C11 +{ + static abstract " + op + @" operator bool(T10 y); +} + +class C11 : I10 where T11 : C11 {} + +interface I12 +{ + static abstract " + op + @" operator bool(int y); +} + +interface I13 +{ + static abstract " + op + @" operator bool(I13 y); +} + +interface I14 where T14 : I14 +{ + abstract static " + op + @" operator T14(object y); +} + +class C15 {} +class C16 : C15 {} + +interface I17 where T17 : C15, I17 +{ + abstract static " + op + @" operator T17(C16 y); +} + +interface I18 where T18 : C16, I18 +{ + abstract static " + op + @" operator T18(C15 y); +} + +interface I19 where T19_1 : I19, T19_2 +{ + abstract static " + op + @" operator T19_2(T19_1 y); +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + compilation1.VerifyDiagnostics( + // (4,39): error CS0555: User-defined operator cannot convert a type to itself + // abstract static explicit operator T1(T1 y); + Diagnostic(ErrorCode.ERR_IdentityConversion, "T1").WithLocation(4, 39), + // (9,39): error CS1964: 'I2.explicit operator T2(dynamic)': user-defined conversions to or from the dynamic type are not allowed + // abstract static explicit operator T2(dynamic y); + Diagnostic(ErrorCode.ERR_BadDynamicConversion, "T2").WithArguments("I2." + op + " operator T2(dynamic)").WithLocation(9, 39), + // (26,43): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // static abstract explicit operator bool(T5 y); + Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "bool").WithLocation(26, 43), + // (32,39): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // static abstract explicit operator bool(T71 y); + Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "bool").WithLocation(32, 39), + // (37,39): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // static abstract explicit operator bool(T8 y); + Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "bool").WithLocation(37, 39), + // (44,39): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // static abstract explicit operator bool(T10 y); + Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "bool").WithLocation(44, 39), + // (47,18): error CS0535: 'C11' does not implement interface member 'I10.explicit operator bool(T11)' + // class C11 : I10 where T11 : C11 {} + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I10").WithArguments("C11", "I10." + op + " operator bool(T11)").WithLocation(47, 18), + // (51,39): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // static abstract explicit operator bool(int y); + Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "bool").WithLocation(51, 39), + // (56,39): error CS0552: 'I13.explicit operator bool(I13)': user-defined conversions to or from an interface are not allowed + // static abstract explicit operator bool(I13 y); + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "bool").WithArguments("I13." + op + " operator bool(I13)").WithLocation(56, 39) + ); + } + [Fact] public void ConsumeAbstractStaticMethod_01() { diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs index 7c8abed264ba7..db27832738ef7 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs @@ -10360,11 +10360,11 @@ public struct S "; var comp = CreateCompilation(text); comp.VerifyDiagnostics( -// (4,37): error CS0555: User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type +// (4,37): error CS0555: User-defined operator cannot convert a type to itself // public static implicit operator MyClass(MyClass aa) // CS0555 Diagnostic(ErrorCode.ERR_IdentityConversion, "MyClass"), -// (11,37): error CS0555: User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type +// (11,37): error CS0555: User-defined operator cannot convert a type to itself // public static implicit operator S?(S s) { return s; } Diagnostic(ErrorCode.ERR_IdentityConversion, "S?") ); From e6a895a14f4d93517f7c2569b82ed42445c55c06 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Wed, 19 May 2021 14:55:50 -0700 Subject: [PATCH 083/127] Add remote to unit tests --- .../AbstractBaseValueTrackingTests.cs | 8 ++ .../ValueTracking/CSharpValueTrackingTests.cs | 127 ++++++++++-------- .../VisualBasicValueTrackingTests.cs | 53 +++++--- .../Remote/InProcRemostHostClient.cs | 1 + 4 files changed, 115 insertions(+), 74 deletions(-) diff --git a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs index a7495e6c31dc0..aa89642307f9d 100644 --- a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs @@ -7,7 +7,9 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.ValueTracking; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.Remote.Testing; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Test.Utilities; using System.Threading; using Microsoft.CodeAnalysis.Text; using Xunit; @@ -17,6 +19,12 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking { public abstract class AbstractBaseValueTrackingTests { + protected TestWorkspace CreateWorkspace(string code, TestHost testHost) + => CreateWorkspace(code, EditorTestCompositions.EditorFeatures.WithTestHostParts(testHost)); + + protected abstract TestWorkspace CreateWorkspace(string code, TestComposition composition); + + internal static async Task> GetTrackedItemsAsync(TestWorkspace testWorkspace, CancellationToken cancellationToken = default) { var cursorDocument = testWorkspace.DocumentWithCursor; diff --git a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs index 19174c8d90ef0..a738d4c001c52 100644 --- a/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/CSharpValueTrackingTests.cs @@ -2,19 +2,24 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; -using Xunit; +using Microsoft.CodeAnalysis.Remote.Testing; using Microsoft.CodeAnalysis.Test.Utilities; -using System.Linq; +using Xunit; namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking { [UseExportProvider] public class CSharpValueTrackingTests : AbstractBaseValueTrackingTests { - [Fact] - public async Task TestProperty() + protected override TestWorkspace CreateWorkspace(string code, TestComposition composition) + => TestWorkspace.CreateCSharp(code, composition: composition); + + [Theory] + [CombinatorialData] + public async Task TestProperty(TestHost testHost) { var code = @" @@ -30,7 +35,7 @@ public void SetS(string s) public string GetS() => S; } "; - using var workspace = TestWorkspace.CreateCSharp(code); + using var workspace = CreateWorkspace(code, testHost); // // property S @@ -46,8 +51,9 @@ await ValidateItemsAsync( }); } - [Fact] - public async Task TestPropertyWithThis() + [Theory] + [CombinatorialData] + public async Task TestPropertyWithThis(TestHost testHost) { var code = @" @@ -63,7 +69,7 @@ public void SetS(string s) public string GetS() => this.S; } "; - using var workspace = TestWorkspace.CreateCSharp(code); + using var workspace = CreateWorkspace(code, testHost); // // property S @@ -79,8 +85,9 @@ await ValidateItemsAsync( }); } - [Fact] - public async Task TestField() + [Theory] + [CombinatorialData] + public async Task TestField(TestHost testHost) { var code = @" @@ -95,7 +102,7 @@ public void SetS(string s) public string GetS() => _s; }"; - using var workspace = TestWorkspace.CreateCSharp(code); + using var workspace = CreateWorkspace(code, testHost); var initialItems = await GetTrackedItemsAsync(workspace); // @@ -112,8 +119,9 @@ await ValidateItemsAsync( }); } - [Fact] - public async Task TestFieldWithThis() + [Theory] + [CombinatorialData] + public async Task TestFieldWithThis(TestHost testHost) { var code = @" @@ -128,7 +136,7 @@ public void SetS(string s) public string GetS() => this._s; }"; - using var workspace = TestWorkspace.CreateCSharp(code); + using var workspace = CreateWorkspace(code, testHost); var initialItems = await GetTrackedItemsAsync(workspace); // @@ -145,8 +153,9 @@ await ValidateItemsAsync( }); } - [Fact] - public async Task TestLocal() + [Theory] + [CombinatorialData] + public async Task TestLocal(TestHost testHost) { var code = @" @@ -159,7 +168,7 @@ public int Add(int x, int y) return z; } }"; - using var workspace = TestWorkspace.CreateCSharp(code); + using var workspace = CreateWorkspace(code, testHost); var initialItems = await GetTrackedItemsAsync(workspace); // @@ -172,8 +181,9 @@ public int Add(int x, int y) ValidateItem(initialItems[1], 5); } - [Fact] - public async Task TestParameter() + [Theory] + [CombinatorialData] + public async Task TestParameter(TestHost testHost) { var code = @" @@ -185,7 +195,7 @@ public int Add(int $$x, int y) return x; } }"; - using var workspace = TestWorkspace.CreateCSharp(code); + using var workspace = CreateWorkspace(code, testHost); var initialItems = await GetTrackedItemsAsync(workspace); // @@ -198,8 +208,9 @@ public int Add(int $$x, int y) ValidateItem(initialItems[1], 3); } - [Fact] - public async Task TestMissingOnMethod() + [Theory] + [CombinatorialData] + public async Task TestMissingOnMethod(TestHost testHost) { var code = @" @@ -211,13 +222,14 @@ class C return x; } }"; - using var workspace = TestWorkspace.CreateCSharp(code); + using var workspace = CreateWorkspace(code, testHost); var initialItems = await GetTrackedItemsAsync(workspace); Assert.Empty(initialItems); } - [Fact] - public async Task TestMissingOnClass() + [Theory] + [CombinatorialData] + public async Task TestMissingOnClass(TestHost testHost) { var code = @" @@ -229,13 +241,14 @@ public int Add(int x, int y) return x; } }"; - using var workspace = TestWorkspace.CreateCSharp(code); + using var workspace = CreateWorkspace(code, testHost); var initialItems = await GetTrackedItemsAsync(workspace); Assert.Empty(initialItems); } - [Fact] - public async Task TestMissingOnNamespace() + [Theory] + [CombinatorialData] + public async Task TestMissingOnNamespace(TestHost testHost) { var code = @" @@ -250,13 +263,14 @@ public int Add(int x, int y) } } }"; - using var workspace = TestWorkspace.CreateCSharp(code); + using var workspace = CreateWorkspace(code, testHost); var initialItems = await GetTrackedItemsAsync(workspace); Assert.Empty(initialItems); } - [Fact] - public async Task MethodTracking1() + [Theory] + [CombinatorialData] + public async Task MethodTracking1(TestHost testHost) { var code = @" @@ -300,7 +314,7 @@ private string CalculateDefault(C c) } } "; - using var workspace = TestWorkspace.CreateCSharp(code); + using var workspace = CreateWorkspace(code, testHost); var initialItems = await GetTrackedItemsAsync(workspace); // @@ -365,8 +379,9 @@ private string CalculateDefault(C c) } } - [Fact] - public async Task MethodTracking2() + [Theory] + [CombinatorialData] + public async Task MethodTracking2(TestHost testHost) { var code = @" @@ -426,7 +441,7 @@ public static void Main(string[] args) } } "; - using var workspace = TestWorkspace.CreateCSharp(code); + using var workspace = CreateWorkspace(code, testHost); var initialItems = await GetTrackedItemsAsync(workspace); // @@ -532,8 +547,9 @@ public static void Main(string[] args) await ValidateChildrenEmptyAsync(workspace, items[4]); } - [Fact] - public async Task MethodTracking3() + [Theory] + [CombinatorialData] + public async Task MethodTracking3(TestHost testHost) { var code = @" @@ -566,7 +582,7 @@ public async Task Double(int x) // |> [|Task.FromResult|](Add(x, y)) [Code.cs:13] // |> Task.FromResult([|Add(x, y)|]) [Code.cs:13] // |> return x [Code.cs:11] - using var workspace = TestWorkspace.CreateCSharp(code); + using var workspace = CreateWorkspace(code, testHost); var initialItems = await GetTrackedItemsAsync(workspace); Assert.Equal(1, initialItems.Length); ValidateItem(initialItems.Single(), 18, "x"); // |> return [|x|] [Code.cs:18] @@ -606,8 +622,9 @@ public async Task Double(int x) }); } - [Fact] - public async Task OutParam() + [Theory] + [CombinatorialData] + public async Task OutParam(TestHost testHost) { var code = @" class C @@ -644,7 +661,7 @@ void M() // |> if (TryConvertInt(o, out [|i|])) [Code.cs:18] // |> if (int.TryParse(o.ToString(), out [|i|])) [Code.cs:5] // |> int i = 0 [Code.cs:15] - using var workspace = TestWorkspace.CreateCSharp(code); + using var workspace = CreateWorkspace(code, testHost); var initialItems = await GetTrackedItemsAsync(workspace); Assert.Equal(1, initialItems.Length); @@ -677,8 +694,9 @@ void M() await ValidateChildrenEmptyAsync(workspace, children.Single()); } - [Fact] - public async Task TestVariableReferenceStart() + [Theory] + [CombinatorialData] + public async Task TestVariableReferenceStart(TestHost testHost) { var code = @" @@ -703,7 +721,7 @@ public static int GetM() // |> int x = GetM() [Code.cs:5] // |> return x; [Code.cs:13] // |> var x = 0; [Code.cs:12] - using var workspace = TestWorkspace.CreateCSharp(code); + using var workspace = CreateWorkspace(code, testHost); var items = await ValidateItemsAsync( workspace, @@ -739,8 +757,9 @@ public static int GetM() await ValidateChildrenEmptyAsync(workspace, items.Single()); } - [Fact] - public async Task TestVariableReferenceStart2() + [Theory] + [CombinatorialData] + public async Task TestVariableReferenceStart2(TestHost testHost) { var code = @" @@ -765,7 +784,7 @@ public static int GetM() // |> int x = GetM() [Code.cs:5] // |> return x; [Code.cs:13] // |> var x = 0; [Code.cs:12] - using var workspace = TestWorkspace.CreateCSharp(code); + using var workspace = CreateWorkspace(code, testHost); var items = await ValidateItemsAsync( workspace, @@ -801,8 +820,9 @@ public static int GetM() await ValidateChildrenEmptyAsync(workspace, items.Single()); } - [Fact] - public async Task TestVariableReferenceStart3() + [Theory] + [CombinatorialData] + public async Task TestVariableReferenceStart3(TestHost testHost) { var code = @" @@ -831,7 +851,7 @@ public static int GetM() // |> return x; [Code.cs:13] // |> var x = 0; [Code.cs:12] // |> x += 1 [Code.cs:8] - using var workspace = TestWorkspace.CreateCSharp(code); + using var workspace = CreateWorkspace(code, testHost); var items = await ValidateItemsAsync( workspace, @@ -870,8 +890,9 @@ public static int GetM() await ValidateChildrenEmptyAsync(workspace, items.Single()); } - [Fact] - public async Task TestMultipleDeclarators() + [Theory] + [CombinatorialData] + public async Task TestMultipleDeclarators(TestHost testHost) { var code = @" @@ -900,7 +921,7 @@ public static int GetM() // |> return x; [Code.cs:13] // |> var x = 0; [Code.cs:12] // |> x += 1 [Code.cs:8] - using var workspace = TestWorkspace.CreateCSharp(code); + using var workspace = CreateWorkspace(code, testHost); var items = await ValidateItemsAsync( workspace, diff --git a/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs index 24c24ca9cc16c..96cd454d6ffb0 100644 --- a/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/VisualBasicValueTrackingTests.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.Remote.Testing; using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; @@ -13,8 +14,12 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking [UseExportProvider] public class VisualBasicValueTrackingTests : AbstractBaseValueTrackingTests { - [Fact] - public async Task TestProperty() + protected override TestWorkspace CreateWorkspace(string code, TestComposition composition) + => TestWorkspace.CreateVisualBasic(code, composition: composition); + + [Theory] + [CombinatorialData] + public async Task TestProperty(TestHost testHost) { var code = @" @@ -40,7 +45,7 @@ End Function End Class "; - using var workspace = TestWorkspace.CreateVisualBasic(code); + using var workspace = CreateWorkspace(code, testHost); var initialItems = await GetTrackedItemsAsync(workspace); // @@ -53,8 +58,9 @@ End Class ValidateItem(initialItems[1], 3); } - [Fact] - public async Task TestField() + [Theory] + [CombinatorialData] + public async Task TestField(TestHost testHost) { var code = @" @@ -71,7 +77,7 @@ End Function End Class "; - using var workspace = TestWorkspace.CreateVisualBasic(code); + using var workspace = CreateWorkspace(code, testHost); var initialItems = await GetTrackedItemsAsync(workspace); // @@ -88,8 +94,9 @@ await ValidateItemsAsync( }); } - [Fact] - public async Task TestLocal() + [Theory] + [CombinatorialData] + public async Task TestLocal(TestHost testHost) { var code = @" @@ -102,7 +109,7 @@ End Function End Class "; - using var workspace = TestWorkspace.CreateVisualBasic(code); + using var workspace = CreateWorkspace(code, testHost); var initialItems = await GetTrackedItemsAsync(workspace); // @@ -115,8 +122,9 @@ End Class ValidateItem(initialItems[1], 3); } - [Fact] - public async Task TestParameter() + [Theory] + [CombinatorialData] + public async Task TestParameter(TestHost testHost) { var code = @" @@ -128,7 +136,7 @@ End Function End Class "; - using var workspace = TestWorkspace.CreateVisualBasic(code); + using var workspace = CreateWorkspace(code, testHost); var initialItems = await GetTrackedItemsAsync(workspace); // @@ -141,8 +149,9 @@ End Class ValidateItem(initialItems[1], 2); } - [Fact] - public async Task TestVariableReferenceStart() + [Theory] + [CombinatorialData] + public async Task TestVariableReferenceStart(TestHost testHost) { var code = @" @@ -164,7 +173,7 @@ End Function // |> Dim x = GetM() [Code.vb:5] // |> Return x; [Code.vb:13] // |> Dim x = 0; [Code.vb:12] - using var workspace = TestWorkspace.CreateVisualBasic(code); + using var workspace = CreateWorkspace(code, testHost); var items = await ValidateItemsAsync( workspace, @@ -200,8 +209,9 @@ End Function await ValidateChildrenEmptyAsync(workspace, items.Single()); } - [Fact] - public async Task TestVariableReferenceStart2() + [Theory] + [CombinatorialData] + public async Task TestVariableReferenceStart2(TestHost testHost) { var code = @" @@ -223,7 +233,7 @@ End Function // |> Dim x = GetM() [Code.vb:5] // |> Return x; [Code.vb:13] // |> Dim x = 0; [Code.vb:12] - using var workspace = TestWorkspace.CreateVisualBasic(code); + using var workspace = CreateWorkspace(code, testHost); var items = await ValidateItemsAsync( workspace, @@ -259,8 +269,9 @@ End Function await ValidateChildrenEmptyAsync(workspace, items.Single()); } - [Fact] - public async Task TestMultipleDeclarators() + [Theory] + [CombinatorialData] + public async Task TestMultipleDeclarators(TestHost testHost) { var code = @" @@ -284,7 +295,7 @@ End Function // |> Dim x = GetM(), z = 1, m As Boolean, n As Boolean, o As Boolean [Code.vb:5] // |> Return x; [Code.vb:12] // |> Dim x = 0; [Code.vb:11] - using var workspace = TestWorkspace.CreateVisualBasic(code); + using var workspace = CreateWorkspace(code, testHost); var items = await ValidateItemsAsync( workspace, diff --git a/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs b/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs index 97d561ab1e5fb..ab25da9b38de7 100644 --- a/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs +++ b/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs @@ -270,6 +270,7 @@ public InProcRemoteServices(HostWorkspaceServices workspaceServices, TraceListen RegisterRemoteBrokeredService(new RemoteGlobalNotificationDeliveryService.Factory()); RegisterRemoteBrokeredService(new RemoteCodeLensReferencesService.Factory()); RegisterRemoteBrokeredService(new RemoteEditAndContinueService.Factory()); + RegisterRemoteBrokeredService(new RemoteValueTrackingService.Factory()); } public void Dispose() From 847f9c60edcf75262c4aaa9264419f323955ae6c Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Wed, 19 May 2021 15:07:02 -0700 Subject: [PATCH 084/127] Support syntax for explicit implementation of conversion operators (#53450) --- .../Portable/Generated/CSharp.Generated.g4 | 10 +- .../Syntax.xml.Internal.Generated.cs | 71 +- .../Syntax.xml.Main.Generated.cs | 12 +- .../Syntax.xml.Syntax.Generated.cs | 58 +- .../CSharp/Portable/Parser/LanguageParser.cs | 255 +- .../CSharp/Portable/PublicAPI.Unshipped.txt | 5 + .../ConversionOperatorDeclarationSyntax.cs | 33 + .../Syntax/OperatorDeclarationSyntax.cs | 6 +- .../CSharp/Portable/Syntax/Syntax.xml | 1 + .../CSharp/Portable/Syntax/SyntaxFactory.cs | 43 + .../Generated/Syntax.Test.xml.Generated.cs | 8 +- .../Parsing/MemberDeclarationParsingTests.cs | 2676 ++++++++++++++++- .../Syntax/Parsing/ParserErrorMessageTests.cs | 28 +- .../Test/Syntax/Parsing/ScriptParsingTests.cs | 16 +- 14 files changed, 3064 insertions(+), 158 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/Syntax/ConversionOperatorDeclarationSyntax.cs diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index 669946641d989..8de51a5543b13 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -178,7 +178,11 @@ arrow_expression_clause ; conversion_operator_declaration - : attribute_list* modifier* ('implicit' | 'explicit') 'operator' type parameter_list (block | (arrow_expression_clause ';')) + : attribute_list* modifier* ('implicit' | 'explicit') explicit_interface_specifier? 'operator' type parameter_list (block | (arrow_expression_clause ';')) + ; + +explicit_interface_specifier + : name '.' ; destructor_declaration @@ -189,10 +193,6 @@ method_declaration : attribute_list* modifier* type explicit_interface_specifier? identifier_token type_parameter_list? parameter_list type_parameter_constraint_clause* (block | (arrow_expression_clause ';')) ; -explicit_interface_specifier - : name '.' - ; - type_parameter_list : '<' type_parameter (',' type_parameter)* '>' ; diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs index 4e7913028c390..29c3d906f2a43 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs @@ -24609,6 +24609,7 @@ internal sealed partial class ConversionOperatorDeclarationSyntax : BaseMethodDe internal readonly GreenNode? attributeLists; internal readonly GreenNode? modifiers; internal readonly SyntaxToken implicitOrExplicitKeyword; + internal readonly ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier; internal readonly SyntaxToken operatorKeyword; internal readonly TypeSyntax type; internal readonly ParameterListSyntax parameterList; @@ -24616,10 +24617,10 @@ internal sealed partial class ConversionOperatorDeclarationSyntax : BaseMethodDe internal readonly ArrowExpressionClauseSyntax? expressionBody; internal readonly SyntaxToken? semicolonToken; - internal ConversionOperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, SyntaxToken implicitOrExplicitKeyword, SyntaxToken operatorKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) + internal ConversionOperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, SyntaxToken implicitOrExplicitKeyword, ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, SyntaxToken operatorKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) : base(kind, diagnostics, annotations) { - this.SlotCount = 9; + this.SlotCount = 10; if (attributeLists != null) { this.AdjustFlagsAndWidth(attributeLists); @@ -24632,6 +24633,11 @@ internal ConversionOperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attribu } this.AdjustFlagsAndWidth(implicitOrExplicitKeyword); this.implicitOrExplicitKeyword = implicitOrExplicitKeyword; + if (explicitInterfaceSpecifier != null) + { + this.AdjustFlagsAndWidth(explicitInterfaceSpecifier); + this.explicitInterfaceSpecifier = explicitInterfaceSpecifier; + } this.AdjustFlagsAndWidth(operatorKeyword); this.operatorKeyword = operatorKeyword; this.AdjustFlagsAndWidth(type); @@ -24655,11 +24661,11 @@ internal ConversionOperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attribu } } - internal ConversionOperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, SyntaxToken implicitOrExplicitKeyword, SyntaxToken operatorKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken, SyntaxFactoryContext context) + internal ConversionOperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, SyntaxToken implicitOrExplicitKeyword, ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, SyntaxToken operatorKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken, SyntaxFactoryContext context) : base(kind) { this.SetFactoryContext(context); - this.SlotCount = 9; + this.SlotCount = 10; if (attributeLists != null) { this.AdjustFlagsAndWidth(attributeLists); @@ -24672,6 +24678,11 @@ internal ConversionOperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attribu } this.AdjustFlagsAndWidth(implicitOrExplicitKeyword); this.implicitOrExplicitKeyword = implicitOrExplicitKeyword; + if (explicitInterfaceSpecifier != null) + { + this.AdjustFlagsAndWidth(explicitInterfaceSpecifier); + this.explicitInterfaceSpecifier = explicitInterfaceSpecifier; + } this.AdjustFlagsAndWidth(operatorKeyword); this.operatorKeyword = operatorKeyword; this.AdjustFlagsAndWidth(type); @@ -24695,10 +24706,10 @@ internal ConversionOperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attribu } } - internal ConversionOperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, SyntaxToken implicitOrExplicitKeyword, SyntaxToken operatorKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken) + internal ConversionOperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, SyntaxToken implicitOrExplicitKeyword, ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, SyntaxToken operatorKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken) : base(kind) { - this.SlotCount = 9; + this.SlotCount = 10; if (attributeLists != null) { this.AdjustFlagsAndWidth(attributeLists); @@ -24711,6 +24722,11 @@ internal ConversionOperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attribu } this.AdjustFlagsAndWidth(implicitOrExplicitKeyword); this.implicitOrExplicitKeyword = implicitOrExplicitKeyword; + if (explicitInterfaceSpecifier != null) + { + this.AdjustFlagsAndWidth(explicitInterfaceSpecifier); + this.explicitInterfaceSpecifier = explicitInterfaceSpecifier; + } this.AdjustFlagsAndWidth(operatorKeyword); this.operatorKeyword = operatorKeyword; this.AdjustFlagsAndWidth(type); @@ -24738,6 +24754,7 @@ internal ConversionOperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attribu public override Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList Modifiers => new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(this.modifiers); /// Gets the "implicit" or "explicit" token. public SyntaxToken ImplicitOrExplicitKeyword => this.implicitOrExplicitKeyword; + public ExplicitInterfaceSpecifierSyntax? ExplicitInterfaceSpecifier => this.explicitInterfaceSpecifier; /// Gets the "operator" token. public SyntaxToken OperatorKeyword => this.operatorKeyword; /// Gets the type. @@ -24754,12 +24771,13 @@ internal ConversionOperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attribu 0 => this.attributeLists, 1 => this.modifiers, 2 => this.implicitOrExplicitKeyword, - 3 => this.operatorKeyword, - 4 => this.type, - 5 => this.parameterList, - 6 => this.body, - 7 => this.expressionBody, - 8 => this.semicolonToken, + 3 => this.explicitInterfaceSpecifier, + 4 => this.operatorKeyword, + 5 => this.type, + 6 => this.parameterList, + 7 => this.body, + 8 => this.expressionBody, + 9 => this.semicolonToken, _ => null, }; @@ -24768,11 +24786,11 @@ internal ConversionOperatorDeclarationSyntax(SyntaxKind kind, GreenNode? attribu public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitConversionOperatorDeclaration(this); public override TResult Accept(CSharpSyntaxVisitor visitor) => visitor.VisitConversionOperatorDeclaration(this); - public ConversionOperatorDeclarationSyntax Update(Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList attributeLists, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList modifiers, SyntaxToken implicitOrExplicitKeyword, SyntaxToken operatorKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax body, ArrowExpressionClauseSyntax expressionBody, SyntaxToken semicolonToken) + public ConversionOperatorDeclarationSyntax Update(Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList attributeLists, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList modifiers, SyntaxToken implicitOrExplicitKeyword, ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier, SyntaxToken operatorKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax body, ArrowExpressionClauseSyntax expressionBody, SyntaxToken semicolonToken) { - if (attributeLists != this.AttributeLists || modifiers != this.Modifiers || implicitOrExplicitKeyword != this.ImplicitOrExplicitKeyword || operatorKeyword != this.OperatorKeyword || type != this.Type || parameterList != this.ParameterList || body != this.Body || expressionBody != this.ExpressionBody || semicolonToken != this.SemicolonToken) + if (attributeLists != this.AttributeLists || modifiers != this.Modifiers || implicitOrExplicitKeyword != this.ImplicitOrExplicitKeyword || explicitInterfaceSpecifier != this.ExplicitInterfaceSpecifier || operatorKeyword != this.OperatorKeyword || type != this.Type || parameterList != this.ParameterList || body != this.Body || expressionBody != this.ExpressionBody || semicolonToken != this.SemicolonToken) { - var newNode = SyntaxFactory.ConversionOperatorDeclaration(attributeLists, modifiers, implicitOrExplicitKeyword, operatorKeyword, type, parameterList, body, expressionBody, semicolonToken); + var newNode = SyntaxFactory.ConversionOperatorDeclaration(attributeLists, modifiers, implicitOrExplicitKeyword, explicitInterfaceSpecifier, operatorKeyword, type, parameterList, body, expressionBody, semicolonToken); var diags = GetDiagnostics(); if (diags?.Length > 0) newNode = newNode.WithDiagnosticsGreen(diags); @@ -24786,15 +24804,15 @@ public ConversionOperatorDeclarationSyntax Update(Microsoft.CodeAnalysis.Syntax. } internal override GreenNode SetDiagnostics(DiagnosticInfo[]? diagnostics) - => new ConversionOperatorDeclarationSyntax(this.Kind, this.attributeLists, this.modifiers, this.implicitOrExplicitKeyword, this.operatorKeyword, this.type, this.parameterList, this.body, this.expressionBody, this.semicolonToken, diagnostics, GetAnnotations()); + => new ConversionOperatorDeclarationSyntax(this.Kind, this.attributeLists, this.modifiers, this.implicitOrExplicitKeyword, this.explicitInterfaceSpecifier, this.operatorKeyword, this.type, this.parameterList, this.body, this.expressionBody, this.semicolonToken, diagnostics, GetAnnotations()); internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations) - => new ConversionOperatorDeclarationSyntax(this.Kind, this.attributeLists, this.modifiers, this.implicitOrExplicitKeyword, this.operatorKeyword, this.type, this.parameterList, this.body, this.expressionBody, this.semicolonToken, GetDiagnostics(), annotations); + => new ConversionOperatorDeclarationSyntax(this.Kind, this.attributeLists, this.modifiers, this.implicitOrExplicitKeyword, this.explicitInterfaceSpecifier, this.operatorKeyword, this.type, this.parameterList, this.body, this.expressionBody, this.semicolonToken, GetDiagnostics(), annotations); internal ConversionOperatorDeclarationSyntax(ObjectReader reader) : base(reader) { - this.SlotCount = 9; + this.SlotCount = 10; var attributeLists = (GreenNode?)reader.ReadValue(); if (attributeLists != null) { @@ -24810,6 +24828,12 @@ internal ConversionOperatorDeclarationSyntax(ObjectReader reader) var implicitOrExplicitKeyword = (SyntaxToken)reader.ReadValue(); AdjustFlagsAndWidth(implicitOrExplicitKeyword); this.implicitOrExplicitKeyword = implicitOrExplicitKeyword; + var explicitInterfaceSpecifier = (ExplicitInterfaceSpecifierSyntax?)reader.ReadValue(); + if (explicitInterfaceSpecifier != null) + { + AdjustFlagsAndWidth(explicitInterfaceSpecifier); + this.explicitInterfaceSpecifier = explicitInterfaceSpecifier; + } var operatorKeyword = (SyntaxToken)reader.ReadValue(); AdjustFlagsAndWidth(operatorKeyword); this.operatorKeyword = operatorKeyword; @@ -24845,6 +24869,7 @@ internal override void WriteTo(ObjectWriter writer) writer.WriteValue(this.attributeLists); writer.WriteValue(this.modifiers); writer.WriteValue(this.implicitOrExplicitKeyword); + writer.WriteValue(this.explicitInterfaceSpecifier); writer.WriteValue(this.operatorKeyword); writer.WriteValue(this.type); writer.WriteValue(this.parameterList); @@ -34076,7 +34101,7 @@ public override CSharpSyntaxNode VisitOperatorDeclaration(OperatorDeclarationSyn => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), (TypeSyntax)Visit(node.ReturnType), (ExplicitInterfaceSpecifierSyntax)Visit(node.ExplicitInterfaceSpecifier), (SyntaxToken)Visit(node.OperatorKeyword), (SyntaxToken)Visit(node.OperatorToken), (ParameterListSyntax)Visit(node.ParameterList), (BlockSyntax)Visit(node.Body), (ArrowExpressionClauseSyntax)Visit(node.ExpressionBody), (SyntaxToken)Visit(node.SemicolonToken)); public override CSharpSyntaxNode VisitConversionOperatorDeclaration(ConversionOperatorDeclarationSyntax node) - => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), (SyntaxToken)Visit(node.ImplicitOrExplicitKeyword), (SyntaxToken)Visit(node.OperatorKeyword), (TypeSyntax)Visit(node.Type), (ParameterListSyntax)Visit(node.ParameterList), (BlockSyntax)Visit(node.Body), (ArrowExpressionClauseSyntax)Visit(node.ExpressionBody), (SyntaxToken)Visit(node.SemicolonToken)); + => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), (SyntaxToken)Visit(node.ImplicitOrExplicitKeyword), (ExplicitInterfaceSpecifierSyntax)Visit(node.ExplicitInterfaceSpecifier), (SyntaxToken)Visit(node.OperatorKeyword), (TypeSyntax)Visit(node.Type), (ParameterListSyntax)Visit(node.ParameterList), (BlockSyntax)Visit(node.Body), (ArrowExpressionClauseSyntax)Visit(node.ExpressionBody), (SyntaxToken)Visit(node.SemicolonToken)); public override CSharpSyntaxNode VisitConstructorDeclaration(ConstructorDeclarationSyntax node) => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), (SyntaxToken)Visit(node.Identifier), (ParameterListSyntax)Visit(node.ParameterList), (ConstructorInitializerSyntax)Visit(node.Initializer), (BlockSyntax)Visit(node.Body), (ArrowExpressionClauseSyntax)Visit(node.ExpressionBody), (SyntaxToken)Visit(node.SemicolonToken)); @@ -37992,7 +38017,7 @@ public OperatorDeclarationSyntax OperatorDeclaration(Microsoft.CodeAnalysis.Synt return new OperatorDeclarationSyntax(SyntaxKind.OperatorDeclaration, attributeLists.Node, modifiers.Node, returnType, explicitInterfaceSpecifier, operatorKeyword, operatorToken, parameterList, body, expressionBody, semicolonToken, this.context); } - public ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration(Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList attributeLists, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList modifiers, SyntaxToken implicitOrExplicitKeyword, SyntaxToken operatorKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken) + public ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration(Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList attributeLists, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList modifiers, SyntaxToken implicitOrExplicitKeyword, ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, SyntaxToken operatorKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken) { #if DEBUG if (implicitOrExplicitKeyword == null) throw new ArgumentNullException(nameof(implicitOrExplicitKeyword)); @@ -38017,7 +38042,7 @@ public ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration(Microso } #endif - return new ConversionOperatorDeclarationSyntax(SyntaxKind.ConversionOperatorDeclaration, attributeLists.Node, modifiers.Node, implicitOrExplicitKeyword, operatorKeyword, type, parameterList, body, expressionBody, semicolonToken, this.context); + return new ConversionOperatorDeclarationSyntax(SyntaxKind.ConversionOperatorDeclaration, attributeLists.Node, modifiers.Node, implicitOrExplicitKeyword, explicitInterfaceSpecifier, operatorKeyword, type, parameterList, body, expressionBody, semicolonToken, this.context); } public ConstructorDeclarationSyntax ConstructorDeclaration(Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList attributeLists, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList modifiers, SyntaxToken identifier, ParameterListSyntax parameterList, ConstructorInitializerSyntax? initializer, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken) @@ -42886,7 +42911,7 @@ public static OperatorDeclarationSyntax OperatorDeclaration(Microsoft.CodeAnalys return new OperatorDeclarationSyntax(SyntaxKind.OperatorDeclaration, attributeLists.Node, modifiers.Node, returnType, explicitInterfaceSpecifier, operatorKeyword, operatorToken, parameterList, body, expressionBody, semicolonToken); } - public static ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration(Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList attributeLists, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList modifiers, SyntaxToken implicitOrExplicitKeyword, SyntaxToken operatorKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken) + public static ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration(Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList attributeLists, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList modifiers, SyntaxToken implicitOrExplicitKeyword, ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, SyntaxToken operatorKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken) { #if DEBUG if (implicitOrExplicitKeyword == null) throw new ArgumentNullException(nameof(implicitOrExplicitKeyword)); @@ -42911,7 +42936,7 @@ public static ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration( } #endif - return new ConversionOperatorDeclarationSyntax(SyntaxKind.ConversionOperatorDeclaration, attributeLists.Node, modifiers.Node, implicitOrExplicitKeyword, operatorKeyword, type, parameterList, body, expressionBody, semicolonToken); + return new ConversionOperatorDeclarationSyntax(SyntaxKind.ConversionOperatorDeclaration, attributeLists.Node, modifiers.Node, implicitOrExplicitKeyword, explicitInterfaceSpecifier, operatorKeyword, type, parameterList, body, expressionBody, semicolonToken); } public static ConstructorDeclarationSyntax ConstructorDeclaration(Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList attributeLists, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList modifiers, SyntaxToken identifier, ParameterListSyntax parameterList, ConstructorInitializerSyntax? initializer, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken? semicolonToken) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs index b8f29a2034fe9..36e1754d02beb 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs @@ -1930,7 +1930,7 @@ public partial class CSharpSyntaxRewriter : CSharpSyntaxVisitor => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), (TypeSyntax?)Visit(node.ReturnType) ?? throw new ArgumentNullException("returnType"), (ExplicitInterfaceSpecifierSyntax?)Visit(node.ExplicitInterfaceSpecifier), VisitToken(node.OperatorKeyword), VisitToken(node.OperatorToken), (ParameterListSyntax?)Visit(node.ParameterList) ?? throw new ArgumentNullException("parameterList"), (BlockSyntax?)Visit(node.Body), (ArrowExpressionClauseSyntax?)Visit(node.ExpressionBody), VisitToken(node.SemicolonToken)); public override SyntaxNode? VisitConversionOperatorDeclaration(ConversionOperatorDeclarationSyntax node) - => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), VisitToken(node.ImplicitOrExplicitKeyword), VisitToken(node.OperatorKeyword), (TypeSyntax?)Visit(node.Type) ?? throw new ArgumentNullException("type"), (ParameterListSyntax?)Visit(node.ParameterList) ?? throw new ArgumentNullException("parameterList"), (BlockSyntax?)Visit(node.Body), (ArrowExpressionClauseSyntax?)Visit(node.ExpressionBody), VisitToken(node.SemicolonToken)); + => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), VisitToken(node.ImplicitOrExplicitKeyword), (ExplicitInterfaceSpecifierSyntax?)Visit(node.ExplicitInterfaceSpecifier), VisitToken(node.OperatorKeyword), (TypeSyntax?)Visit(node.Type) ?? throw new ArgumentNullException("type"), (ParameterListSyntax?)Visit(node.ParameterList) ?? throw new ArgumentNullException("parameterList"), (BlockSyntax?)Visit(node.Body), (ArrowExpressionClauseSyntax?)Visit(node.ExpressionBody), VisitToken(node.SemicolonToken)); public override SyntaxNode? VisitConstructorDeclaration(ConstructorDeclarationSyntax node) => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), VisitToken(node.Identifier), (ParameterListSyntax?)Visit(node.ParameterList) ?? throw new ArgumentNullException("parameterList"), (ConstructorInitializerSyntax?)Visit(node.Initializer), (BlockSyntax?)Visit(node.Body), (ArrowExpressionClauseSyntax?)Visit(node.ExpressionBody), VisitToken(node.SemicolonToken)); @@ -5171,7 +5171,7 @@ public static OperatorDeclarationSyntax OperatorDeclaration(TypeSyntax returnTyp => SyntaxFactory.OperatorDeclaration(default, default(SyntaxTokenList), returnType, default, SyntaxFactory.Token(SyntaxKind.OperatorKeyword), operatorToken, SyntaxFactory.ParameterList(), default, default, default); /// Creates a new ConversionOperatorDeclarationSyntax instance. - public static ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration(SyntaxList attributeLists, SyntaxTokenList modifiers, SyntaxToken implicitOrExplicitKeyword, SyntaxToken operatorKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken semicolonToken) + public static ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration(SyntaxList attributeLists, SyntaxTokenList modifiers, SyntaxToken implicitOrExplicitKeyword, ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, SyntaxToken operatorKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken semicolonToken) { switch (implicitOrExplicitKeyword.Kind()) { @@ -5188,16 +5188,16 @@ public static ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration( case SyntaxKind.None: break; default: throw new ArgumentException(nameof(semicolonToken)); } - return (ConversionOperatorDeclarationSyntax)Syntax.InternalSyntax.SyntaxFactory.ConversionOperatorDeclaration(attributeLists.Node.ToGreenList(), modifiers.Node.ToGreenList(), (Syntax.InternalSyntax.SyntaxToken)implicitOrExplicitKeyword.Node!, (Syntax.InternalSyntax.SyntaxToken)operatorKeyword.Node!, (Syntax.InternalSyntax.TypeSyntax)type.Green, (Syntax.InternalSyntax.ParameterListSyntax)parameterList.Green, body == null ? null : (Syntax.InternalSyntax.BlockSyntax)body.Green, expressionBody == null ? null : (Syntax.InternalSyntax.ArrowExpressionClauseSyntax)expressionBody.Green, (Syntax.InternalSyntax.SyntaxToken?)semicolonToken.Node).CreateRed(); + return (ConversionOperatorDeclarationSyntax)Syntax.InternalSyntax.SyntaxFactory.ConversionOperatorDeclaration(attributeLists.Node.ToGreenList(), modifiers.Node.ToGreenList(), (Syntax.InternalSyntax.SyntaxToken)implicitOrExplicitKeyword.Node!, explicitInterfaceSpecifier == null ? null : (Syntax.InternalSyntax.ExplicitInterfaceSpecifierSyntax)explicitInterfaceSpecifier.Green, (Syntax.InternalSyntax.SyntaxToken)operatorKeyword.Node!, (Syntax.InternalSyntax.TypeSyntax)type.Green, (Syntax.InternalSyntax.ParameterListSyntax)parameterList.Green, body == null ? null : (Syntax.InternalSyntax.BlockSyntax)body.Green, expressionBody == null ? null : (Syntax.InternalSyntax.ArrowExpressionClauseSyntax)expressionBody.Green, (Syntax.InternalSyntax.SyntaxToken?)semicolonToken.Node).CreateRed(); } /// Creates a new ConversionOperatorDeclarationSyntax instance. - public static ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration(SyntaxList attributeLists, SyntaxTokenList modifiers, SyntaxToken implicitOrExplicitKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody) - => SyntaxFactory.ConversionOperatorDeclaration(attributeLists, modifiers, implicitOrExplicitKeyword, SyntaxFactory.Token(SyntaxKind.OperatorKeyword), type, parameterList, body, expressionBody, default); + public static ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration(SyntaxList attributeLists, SyntaxTokenList modifiers, SyntaxToken implicitOrExplicitKeyword, ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody) + => SyntaxFactory.ConversionOperatorDeclaration(attributeLists, modifiers, implicitOrExplicitKeyword, explicitInterfaceSpecifier, SyntaxFactory.Token(SyntaxKind.OperatorKeyword), type, parameterList, body, expressionBody, default); /// Creates a new ConversionOperatorDeclarationSyntax instance. public static ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration(SyntaxToken implicitOrExplicitKeyword, TypeSyntax type) - => SyntaxFactory.ConversionOperatorDeclaration(default, default(SyntaxTokenList), implicitOrExplicitKeyword, SyntaxFactory.Token(SyntaxKind.OperatorKeyword), type, SyntaxFactory.ParameterList(), default, default, default); + => SyntaxFactory.ConversionOperatorDeclaration(default, default(SyntaxTokenList), implicitOrExplicitKeyword, default, SyntaxFactory.Token(SyntaxKind.OperatorKeyword), type, SyntaxFactory.ParameterList(), default, default, default); /// Creates a new ConstructorDeclarationSyntax instance. public static ConstructorDeclarationSyntax ConstructorDeclaration(SyntaxList attributeLists, SyntaxTokenList modifiers, SyntaxToken identifier, ParameterListSyntax parameterList, ConstructorInitializerSyntax? initializer, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken semicolonToken) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs index 068a1962a6d60..eace4be86d0fe 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs @@ -11623,6 +11623,7 @@ public OperatorDeclarationSyntax Update(SyntaxList attribut public sealed partial class ConversionOperatorDeclarationSyntax : BaseMethodDeclarationSyntax { private SyntaxNode? attributeLists; + private ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier; private TypeSyntax? type; private ParameterListSyntax? parameterList; private BlockSyntax? body; @@ -11647,17 +11648,19 @@ public override SyntaxTokenList Modifiers /// Gets the "implicit" or "explicit" token. public SyntaxToken ImplicitOrExplicitKeyword => new SyntaxToken(this, ((Syntax.InternalSyntax.ConversionOperatorDeclarationSyntax)this.Green).implicitOrExplicitKeyword, GetChildPosition(2), GetChildIndex(2)); + public ExplicitInterfaceSpecifierSyntax? ExplicitInterfaceSpecifier => GetRed(ref this.explicitInterfaceSpecifier, 3); + /// Gets the "operator" token. - public SyntaxToken OperatorKeyword => new SyntaxToken(this, ((Syntax.InternalSyntax.ConversionOperatorDeclarationSyntax)this.Green).operatorKeyword, GetChildPosition(3), GetChildIndex(3)); + public SyntaxToken OperatorKeyword => new SyntaxToken(this, ((Syntax.InternalSyntax.ConversionOperatorDeclarationSyntax)this.Green).operatorKeyword, GetChildPosition(4), GetChildIndex(4)); /// Gets the type. - public TypeSyntax Type => GetRed(ref this.type, 4)!; + public TypeSyntax Type => GetRed(ref this.type, 5)!; - public override ParameterListSyntax ParameterList => GetRed(ref this.parameterList, 5)!; + public override ParameterListSyntax ParameterList => GetRed(ref this.parameterList, 6)!; - public override BlockSyntax? Body => GetRed(ref this.body, 6); + public override BlockSyntax? Body => GetRed(ref this.body, 7); - public override ArrowExpressionClauseSyntax? ExpressionBody => GetRed(ref this.expressionBody, 7); + public override ArrowExpressionClauseSyntax? ExpressionBody => GetRed(ref this.expressionBody, 8); /// Gets the optional semicolon token. public override SyntaxToken SemicolonToken @@ -11665,7 +11668,7 @@ public override SyntaxToken SemicolonToken get { var slot = ((Syntax.InternalSyntax.ConversionOperatorDeclarationSyntax)this.Green).semicolonToken; - return slot != null ? new SyntaxToken(this, slot, GetChildPosition(8), GetChildIndex(8)) : default; + return slot != null ? new SyntaxToken(this, slot, GetChildPosition(9), GetChildIndex(9)) : default; } } @@ -11673,10 +11676,11 @@ public override SyntaxToken SemicolonToken => index switch { 0 => GetRedAtZero(ref this.attributeLists)!, - 4 => GetRed(ref this.type, 4)!, - 5 => GetRed(ref this.parameterList, 5)!, - 6 => GetRed(ref this.body, 6), - 7 => GetRed(ref this.expressionBody, 7), + 3 => GetRed(ref this.explicitInterfaceSpecifier, 3), + 5 => GetRed(ref this.type, 5)!, + 6 => GetRed(ref this.parameterList, 6)!, + 7 => GetRed(ref this.body, 7), + 8 => GetRed(ref this.expressionBody, 8), _ => null, }; @@ -11684,21 +11688,22 @@ public override SyntaxToken SemicolonToken => index switch { 0 => this.attributeLists, - 4 => this.type, - 5 => this.parameterList, - 6 => this.body, - 7 => this.expressionBody, + 3 => this.explicitInterfaceSpecifier, + 5 => this.type, + 6 => this.parameterList, + 7 => this.body, + 8 => this.expressionBody, _ => null, }; public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitConversionOperatorDeclaration(this); public override TResult? Accept(CSharpSyntaxVisitor visitor) where TResult : default => visitor.VisitConversionOperatorDeclaration(this); - public ConversionOperatorDeclarationSyntax Update(SyntaxList attributeLists, SyntaxTokenList modifiers, SyntaxToken implicitOrExplicitKeyword, SyntaxToken operatorKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken semicolonToken) + public ConversionOperatorDeclarationSyntax Update(SyntaxList attributeLists, SyntaxTokenList modifiers, SyntaxToken implicitOrExplicitKeyword, ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, SyntaxToken operatorKeyword, TypeSyntax type, ParameterListSyntax parameterList, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, SyntaxToken semicolonToken) { - if (attributeLists != this.AttributeLists || modifiers != this.Modifiers || implicitOrExplicitKeyword != this.ImplicitOrExplicitKeyword || operatorKeyword != this.OperatorKeyword || type != this.Type || parameterList != this.ParameterList || body != this.Body || expressionBody != this.ExpressionBody || semicolonToken != this.SemicolonToken) + if (attributeLists != this.AttributeLists || modifiers != this.Modifiers || implicitOrExplicitKeyword != this.ImplicitOrExplicitKeyword || explicitInterfaceSpecifier != this.ExplicitInterfaceSpecifier || operatorKeyword != this.OperatorKeyword || type != this.Type || parameterList != this.ParameterList || body != this.Body || expressionBody != this.ExpressionBody || semicolonToken != this.SemicolonToken) { - var newNode = SyntaxFactory.ConversionOperatorDeclaration(attributeLists, modifiers, implicitOrExplicitKeyword, operatorKeyword, type, parameterList, body, expressionBody, semicolonToken); + var newNode = SyntaxFactory.ConversionOperatorDeclaration(attributeLists, modifiers, implicitOrExplicitKeyword, explicitInterfaceSpecifier, operatorKeyword, type, parameterList, body, expressionBody, semicolonToken); var annotations = GetAnnotations(); return annotations?.Length > 0 ? newNode.WithAnnotations(annotations) : newNode; } @@ -11707,20 +11712,21 @@ public ConversionOperatorDeclarationSyntax Update(SyntaxList attributeLists) => WithAttributeLists(attributeLists); - public new ConversionOperatorDeclarationSyntax WithAttributeLists(SyntaxList attributeLists) => Update(attributeLists, this.Modifiers, this.ImplicitOrExplicitKeyword, this.OperatorKeyword, this.Type, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); + public new ConversionOperatorDeclarationSyntax WithAttributeLists(SyntaxList attributeLists) => Update(attributeLists, this.Modifiers, this.ImplicitOrExplicitKeyword, this.ExplicitInterfaceSpecifier, this.OperatorKeyword, this.Type, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); internal override MemberDeclarationSyntax WithModifiersCore(SyntaxTokenList modifiers) => WithModifiers(modifiers); - public new ConversionOperatorDeclarationSyntax WithModifiers(SyntaxTokenList modifiers) => Update(this.AttributeLists, modifiers, this.ImplicitOrExplicitKeyword, this.OperatorKeyword, this.Type, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); - public ConversionOperatorDeclarationSyntax WithImplicitOrExplicitKeyword(SyntaxToken implicitOrExplicitKeyword) => Update(this.AttributeLists, this.Modifiers, implicitOrExplicitKeyword, this.OperatorKeyword, this.Type, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); - public ConversionOperatorDeclarationSyntax WithOperatorKeyword(SyntaxToken operatorKeyword) => Update(this.AttributeLists, this.Modifiers, this.ImplicitOrExplicitKeyword, operatorKeyword, this.Type, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); - public ConversionOperatorDeclarationSyntax WithType(TypeSyntax type) => Update(this.AttributeLists, this.Modifiers, this.ImplicitOrExplicitKeyword, this.OperatorKeyword, type, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); + public new ConversionOperatorDeclarationSyntax WithModifiers(SyntaxTokenList modifiers) => Update(this.AttributeLists, modifiers, this.ImplicitOrExplicitKeyword, this.ExplicitInterfaceSpecifier, this.OperatorKeyword, this.Type, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); + public ConversionOperatorDeclarationSyntax WithImplicitOrExplicitKeyword(SyntaxToken implicitOrExplicitKeyword) => Update(this.AttributeLists, this.Modifiers, implicitOrExplicitKeyword, this.ExplicitInterfaceSpecifier, this.OperatorKeyword, this.Type, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); + public ConversionOperatorDeclarationSyntax WithExplicitInterfaceSpecifier(ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier) => Update(this.AttributeLists, this.Modifiers, this.ImplicitOrExplicitKeyword, explicitInterfaceSpecifier, this.OperatorKeyword, this.Type, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); + public ConversionOperatorDeclarationSyntax WithOperatorKeyword(SyntaxToken operatorKeyword) => Update(this.AttributeLists, this.Modifiers, this.ImplicitOrExplicitKeyword, this.ExplicitInterfaceSpecifier, operatorKeyword, this.Type, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); + public ConversionOperatorDeclarationSyntax WithType(TypeSyntax type) => Update(this.AttributeLists, this.Modifiers, this.ImplicitOrExplicitKeyword, this.ExplicitInterfaceSpecifier, this.OperatorKeyword, type, this.ParameterList, this.Body, this.ExpressionBody, this.SemicolonToken); internal override BaseMethodDeclarationSyntax WithParameterListCore(ParameterListSyntax parameterList) => WithParameterList(parameterList); - public new ConversionOperatorDeclarationSyntax WithParameterList(ParameterListSyntax parameterList) => Update(this.AttributeLists, this.Modifiers, this.ImplicitOrExplicitKeyword, this.OperatorKeyword, this.Type, parameterList, this.Body, this.ExpressionBody, this.SemicolonToken); + public new ConversionOperatorDeclarationSyntax WithParameterList(ParameterListSyntax parameterList) => Update(this.AttributeLists, this.Modifiers, this.ImplicitOrExplicitKeyword, this.ExplicitInterfaceSpecifier, this.OperatorKeyword, this.Type, parameterList, this.Body, this.ExpressionBody, this.SemicolonToken); internal override BaseMethodDeclarationSyntax WithBodyCore(BlockSyntax? body) => WithBody(body); - public new ConversionOperatorDeclarationSyntax WithBody(BlockSyntax? body) => Update(this.AttributeLists, this.Modifiers, this.ImplicitOrExplicitKeyword, this.OperatorKeyword, this.Type, this.ParameterList, body, this.ExpressionBody, this.SemicolonToken); + public new ConversionOperatorDeclarationSyntax WithBody(BlockSyntax? body) => Update(this.AttributeLists, this.Modifiers, this.ImplicitOrExplicitKeyword, this.ExplicitInterfaceSpecifier, this.OperatorKeyword, this.Type, this.ParameterList, body, this.ExpressionBody, this.SemicolonToken); internal override BaseMethodDeclarationSyntax WithExpressionBodyCore(ArrowExpressionClauseSyntax? expressionBody) => WithExpressionBody(expressionBody); - public new ConversionOperatorDeclarationSyntax WithExpressionBody(ArrowExpressionClauseSyntax? expressionBody) => Update(this.AttributeLists, this.Modifiers, this.ImplicitOrExplicitKeyword, this.OperatorKeyword, this.Type, this.ParameterList, this.Body, expressionBody, this.SemicolonToken); + public new ConversionOperatorDeclarationSyntax WithExpressionBody(ArrowExpressionClauseSyntax? expressionBody) => Update(this.AttributeLists, this.Modifiers, this.ImplicitOrExplicitKeyword, this.ExplicitInterfaceSpecifier, this.OperatorKeyword, this.Type, this.ParameterList, this.Body, expressionBody, this.SemicolonToken); internal override BaseMethodDeclarationSyntax WithSemicolonTokenCore(SyntaxToken semicolonToken) => WithSemicolonToken(semicolonToken); - public new ConversionOperatorDeclarationSyntax WithSemicolonToken(SyntaxToken semicolonToken) => Update(this.AttributeLists, this.Modifiers, this.ImplicitOrExplicitKeyword, this.OperatorKeyword, this.Type, this.ParameterList, this.Body, this.ExpressionBody, semicolonToken); + public new ConversionOperatorDeclarationSyntax WithSemicolonToken(SyntaxToken semicolonToken) => Update(this.AttributeLists, this.Modifiers, this.ImplicitOrExplicitKeyword, this.ExplicitInterfaceSpecifier, this.OperatorKeyword, this.Type, this.ParameterList, this.Body, this.ExpressionBody, semicolonToken); internal override MemberDeclarationSyntax AddAttributeListsCore(params AttributeListSyntax[] items) => AddAttributeLists(items); public new ConversionOperatorDeclarationSyntax AddAttributeLists(params AttributeListSyntax[] items) => WithAttributeLists(this.AttributeLists.AddRange(items)); diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index eb061d4e2014f..c69864aca4f12 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -2351,11 +2351,10 @@ private MemberDeclarationSyntax ParseMemberDeclarationOrStatementCore(SyntaxKind } // Check for conversion operators (implicit/explicit) - if (this.CurrentToken.Kind == SyntaxKind.ExplicitKeyword || - this.CurrentToken.Kind == SyntaxKind.ImplicitKeyword || - (this.CurrentToken.Kind == SyntaxKind.OperatorKeyword && !SyntaxFacts.IsAnyOverloadableOperator(this.PeekToken(1).Kind))) + result = this.TryParseConversionOperatorDeclaration(attributes, modifiers); + if (result is not null) { - return this.ParseConversionOperatorDeclaration(attributes, modifiers); + return result; } if (this.CurrentToken.Kind == SyntaxKind.NamespaceKeyword) @@ -2773,11 +2772,10 @@ private MemberDeclarationSyntax ParseMemberDeclarationCore(SyntaxKind parentKind } // Check for conversion operators (implicit/explicit) - if (this.CurrentToken.Kind == SyntaxKind.ExplicitKeyword || - this.CurrentToken.Kind == SyntaxKind.ImplicitKeyword || - (this.CurrentToken.Kind == SyntaxKind.OperatorKeyword && !SyntaxFacts.IsAnyOverloadableOperator(this.PeekToken(1).Kind))) + MemberDeclarationSyntax result = this.TryParseConversionOperatorDeclaration(attributes, modifiers); + if (result is not null) { - return this.ParseConversionOperatorDeclaration(attributes, modifiers); + return result; } // Namespaces should be handled by the caller, not checking for them @@ -2799,7 +2797,6 @@ private MemberDeclarationSyntax ParseMemberDeclarationCore(SyntaxKind parentKind { // Check for misplaced modifiers. if we see any, then consider this member // terminated and restart parsing. - MemberDeclarationSyntax result; if (IsMisplacedModifier(modifiers, attributes, type, out result)) { return result; @@ -2922,6 +2919,7 @@ private bool IsFieldDeclaration(bool isEvent) { case SyntaxKind.DotToken: // Goo. explicit case SyntaxKind.ColonColonToken: // Goo:: explicit + case SyntaxKind.DotDotToken: // Goo.. explicit case SyntaxKind.LessThanToken: // Goo< explicit or generic method case SyntaxKind.OpenBraceToken: // Goo { property case SyntaxKind.EqualsGreaterThanToken: // Goo => property @@ -3251,39 +3249,178 @@ private bool IsEndOfReturnType() } } - private ConversionOperatorDeclarationSyntax ParseConversionOperatorDeclaration(SyntaxList attributes, SyntaxListBuilder modifiers) + private ConversionOperatorDeclarationSyntax TryParseConversionOperatorDeclaration(SyntaxList attributes, SyntaxListBuilder modifiers) { - SyntaxToken style; - if (this.CurrentToken.Kind == SyntaxKind.ImplicitKeyword || this.CurrentToken.Kind == SyntaxKind.ExplicitKeyword) + var point = GetResetPoint(); + + try { - style = this.EatToken(); + SyntaxToken style; + if (this.CurrentToken.Kind == SyntaxKind.ImplicitKeyword || this.CurrentToken.Kind == SyntaxKind.ExplicitKeyword) + { + style = this.EatToken(); + } + else + { + style = this.EatToken(SyntaxKind.ExplicitKeyword); + } + + ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt = tryParseExplicitInterfaceSpecifier(); + SyntaxToken opKeyword; + TypeSyntax type; + ParameterListSyntax paramList; + + if (style.IsMissing) + { + if (this.CurrentToken.Kind != SyntaxKind.OperatorKeyword || SyntaxFacts.IsAnyOverloadableOperator(this.PeekToken(1).Kind) || + explicitInterfaceOpt?.DotToken.IsMissing == true) + { + this.Reset(ref point); + return null; + } + } + else if (explicitInterfaceOpt is not null && this.CurrentToken.Kind != SyntaxKind.OperatorKeyword && style.TrailingTrivia.Any((int)SyntaxKind.EndOfLineTrivia)) + { + // Not likely an explicit interface implementation. Likely a begining of the next member on the next line. + this.Reset(ref point); + style = this.EatToken(); + explicitInterfaceOpt = null; + opKeyword = this.EatToken(SyntaxKind.OperatorKeyword); + type = this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_IdentifierExpected); + SyntaxToken open = SyntaxFactory.MissingToken(SyntaxKind.OpenParenToken); + SyntaxToken close = SyntaxFactory.MissingToken(SyntaxKind.CloseParenToken); + var parameters = _pool.AllocateSeparated(); + paramList = _syntaxFactory.ParameterList(open, parameters, close); + _pool.Free(parameters); + + return _syntaxFactory.ConversionOperatorDeclaration( + attributes, + modifiers.ToList(), + style, + explicitInterfaceOpt, + opKeyword, + type, + paramList, + body: null, + expressionBody: null, + semicolonToken: SyntaxFactory.MissingToken(SyntaxKind.SemicolonToken)); + } + + opKeyword = this.EatToken(SyntaxKind.OperatorKeyword); + + this.Release(ref point); + point = GetResetPoint(); + + bool couldBeParameterList = this.CurrentToken.Kind == SyntaxKind.OpenParenToken; + type = this.ParseType(); + + if (couldBeParameterList && type is TupleTypeSyntax { Elements: { Count: 2, SeparatorCount: 1 } } tupleType && + tupleType.Elements.GetSeparator(0).IsMissing && tupleType.Elements[1].IsMissing && + this.CurrentToken.Kind != SyntaxKind.OpenParenToken) + { + // It looks like the type is missing and we parsed parameter list as the type. Recover. + this.Reset(ref point); + type = ParseIdentifierName(); + } + + paramList = this.ParseParenthesizedParameterList(); + + BlockSyntax blockBody; + ArrowExpressionClauseSyntax expressionBody; + SyntaxToken semicolon; + this.ParseBlockAndExpressionBodiesWithSemicolon(out blockBody, out expressionBody, out semicolon); + + return _syntaxFactory.ConversionOperatorDeclaration( + attributes, + modifiers.ToList(), + style, + explicitInterfaceOpt, + opKeyword, + type, + paramList, + blockBody, + expressionBody, + semicolon); } - else + finally { - style = this.EatToken(SyntaxKind.ExplicitKeyword); + this.Release(ref point); } - SyntaxToken opKeyword = this.EatToken(SyntaxKind.OperatorKeyword); + ExplicitInterfaceSpecifierSyntax tryParseExplicitInterfaceSpecifier() + { + if (this.CurrentToken.Kind == SyntaxKind.OperatorKeyword) + { + return null; + } - var type = this.ParseType(); + if (this.CurrentToken.Kind != SyntaxKind.IdentifierToken) + { + return null; + } - var paramList = this.ParseParenthesizedParameterList(); + NameSyntax explicitInterfaceName = null; + SyntaxToken separator = null; - BlockSyntax blockBody; - ArrowExpressionClauseSyntax expressionBody; - SyntaxToken semicolon; - this.ParseBlockAndExpressionBodiesWithSemicolon(out blockBody, out expressionBody, out semicolon); + while (true) + { + // now, scan past the next name. if it's followed by a dot then + // it's part of the explicit name we're building up. Otherwise, + // it should be an operator token + var point = GetResetPoint(); + bool isPartOfInterfaceName; + try + { + if (this.CurrentToken.Kind == SyntaxKind.OperatorKeyword) + { + isPartOfInterfaceName = false; + } + else + { + int lastTokenPosition = -1; + IsMakingProgress(ref lastTokenPosition, assertIfFalse: true); + ScanNamedTypePart(); + isPartOfInterfaceName = IsDotOrColonColonOrDotDot() || + (IsMakingProgress(ref lastTokenPosition, assertIfFalse: false) && this.CurrentToken.Kind != SyntaxKind.OpenParenToken); + } + } + finally + { + this.Reset(ref point); + this.Release(ref point); + } - return _syntaxFactory.ConversionOperatorDeclaration( - attributes, - modifiers.ToList(), - style, - opKeyword, - type, - paramList, - blockBody, - expressionBody, - semicolon); + if (!isPartOfInterfaceName) + { + // We're past any explicit interface portion + if (separator != null && separator.Kind == SyntaxKind.ColonColonToken) + { + separator = this.AddError(separator, ErrorCode.ERR_AliasQualAsExpression); + separator = this.ConvertToMissingWithTrailingTrivia(separator, SyntaxKind.DotToken); + } + + break; + } + else + { + // If we saw a . or :: then we must have something explicit. + AccumulateExplicitInterfaceName(ref explicitInterfaceName, ref separator, reportAnErrorOnMisplacedColonColon: true); + } + } + + if (explicitInterfaceName is null) + { + return null; + } + + if (separator.Kind != SyntaxKind.DotToken) + { + separator = WithAdditionalDiagnostics(separator, GetExpectedTokenError(SyntaxKind.DotToken, separator.Kind, separator.GetLeadingTriviaWidth(), separator.Width)); + separator = ConvertToMissingWithTrailingTrivia(separator, SyntaxKind.DotToken); + } + + return CheckFeatureAvailability(_syntaxFactory.ExplicitInterfaceSpecifier(explicitInterfaceName, separator), MessageID.IDS_FeatureStaticAbstractMembersInInterfaces); + } } private OperatorDeclarationSyntax ParseOperatorDeclaration( @@ -4137,8 +4274,7 @@ private void ParseParameterList( private bool IsEndOfParameterList() { - return this.CurrentToken.Kind == SyntaxKind.CloseParenToken - || this.CurrentToken.Kind == SyntaxKind.CloseBracketToken; + return this.CurrentToken.Kind is SyntaxKind.CloseParenToken or SyntaxKind.CloseBracketToken or SyntaxKind.SemicolonToken; } private PostSkipAction SkipBadParameterListTokens( @@ -6006,7 +6142,7 @@ private void ParseMemberName( try { ScanNamedTypePart(); - isMemberName = !IsDotOrColonColon(); + isMemberName = !IsDotOrColonColonOrDotDot(); } finally { @@ -6117,9 +6253,18 @@ private void AccumulateExplicitInterfaceName(ref NameSyntax explicitInterfaceNam explicitInterfaceName = this.ParseSimpleName(NameOptions.InTypeList); // Now, get the next separator. - separator = this.CurrentToken.Kind == SyntaxKind.ColonColonToken - ? this.EatToken() // fine after the first identifier - : this.EatToken(SyntaxKind.DotToken); + if (this.CurrentToken.Kind == SyntaxKind.DotDotToken) + { + // Error recovery as in ParseQualifiedNameRight. If we have `X..Y` break that into `X..Y` + separator = this.EatToken(); + explicitInterfaceName = RecoverFromDotDot(explicitInterfaceName, ref separator); + } + else + { + separator = this.CurrentToken.Kind == SyntaxKind.ColonColonToken + ? this.EatToken() // fine after the first identifier + : this.EatToken(SyntaxKind.DotToken); + } } else { @@ -6142,6 +6287,12 @@ private void AccumulateExplicitInterfaceName(ref NameSyntax explicitInterfaceNam separator = this.ConvertToMissingWithTrailingTrivia(separator, SyntaxKind.DotToken); } + else if (this.CurrentToken.Kind == SyntaxKind.DotDotToken) + { + // Error recovery as in ParseQualifiedNameRight. If we have `X..Y` break that into `X..Y` + separator = this.EatToken(); + explicitInterfaceName = RecoverFromDotDot(explicitInterfaceName, ref separator); + } else { separator = this.EatToken(SyntaxKind.DotToken); @@ -6197,7 +6348,7 @@ private bool IsOperatorStart(out ExplicitInterfaceSpecifierSyntax explicitInterf // If we have part of the interface name, but no dot before the operator token, then // for the purpose of error recovery, treat this as an operator start with a // missing dot token. - isPartOfInterfaceName = IsDotOrColonColon() || IsOperatorKeyword(); + isPartOfInterfaceName = IsDotOrColonColonOrDotDot() || IsOperatorKeyword(); } } finally @@ -6269,7 +6420,7 @@ private NameSyntax ParseQualifiedName(NameOptions options = NameOptions.None) NameSyntax name = this.ParseAliasQualifiedName(options); // Handle .. tokens for error recovery purposes. - while (this.IsDotOrColonColon() || this.CurrentToken.Kind == SyntaxKind.DotDotToken) + while (IsDotOrColonColonOrDotDot()) { if (this.PeekToken(1).Kind == SyntaxKind.ThisKeyword) { @@ -6283,6 +6434,11 @@ private NameSyntax ParseQualifiedName(NameOptions options = NameOptions.None) return name; } + private bool IsDotOrColonColonOrDotDot() + { + return this.IsDotOrColonColon() || this.CurrentToken.Kind == SyntaxKind.DotDotToken; + } + private NameSyntax ParseQualifiedNameRight( NameOptions options, NameSyntax left, @@ -6300,14 +6456,7 @@ private NameSyntax ParseQualifiedNameRight( return _syntaxFactory.QualifiedName(left, separator, right); case SyntaxKind.DotDotToken: // Error recovery. If we have `X..Y` break that into `X..Y` - - var leftDot = SyntaxFactory.Token(separator.LeadingTrivia.Node, SyntaxKind.DotToken, null); - var missingName = this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_IdentifierExpected); - var rightDot = SyntaxFactory.Token(null, SyntaxKind.DotToken, separator.TrailingTrivia.Node); - - return _syntaxFactory.QualifiedName( - _syntaxFactory.QualifiedName(left, leftDot, missingName), - rightDot, right); + return _syntaxFactory.QualifiedName(RecoverFromDotDot(left, ref separator), separator, right); case SyntaxKind.ColonColonToken: if (left.Kind != SyntaxKind.IdentifierName) @@ -6344,6 +6493,16 @@ private NameSyntax ParseQualifiedNameRight( } } + private NameSyntax RecoverFromDotDot(NameSyntax left, ref SyntaxToken separator) + { + Debug.Assert(separator.Kind == SyntaxKind.DotDotToken); + + var leftDot = SyntaxFactory.Token(separator.LeadingTrivia.Node, SyntaxKind.DotToken, null); + var missingName = this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_IdentifierExpected); + separator = SyntaxFactory.Token(null, SyntaxKind.DotToken, separator.TrailingTrivia.Node); + return _syntaxFactory.QualifiedName(left, leftDot, missingName); + } + private SyntaxToken ConvertToMissingWithTrailingTrivia(SyntaxToken token, SyntaxKind expectedKind) { var newToken = SyntaxFactory.MissingToken(expectedKind); diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index a16f3f039223a..81f87c695de3b 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -31,3 +31,8 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.OperatorDeclaration(Microsoft Microsoft.CodeAnalysis.CSharp.Syntax.OperatorDeclarationSyntax.ExplicitInterfaceSpecifier.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ExplicitInterfaceSpecifierSyntax Microsoft.CodeAnalysis.CSharp.Syntax.OperatorDeclarationSyntax.Update(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.CSharp.Syntax.ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier, Microsoft.CodeAnalysis.SyntaxToken operatorKeyword, Microsoft.CodeAnalysis.SyntaxToken operatorToken, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.OperatorDeclarationSyntax Microsoft.CodeAnalysis.CSharp.Syntax.OperatorDeclarationSyntax.WithExplicitInterfaceSpecifier(Microsoft.CodeAnalysis.CSharp.Syntax.ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.OperatorDeclarationSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ConversionOperatorDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken implicitOrExplicitKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.ConversionOperatorDeclarationSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ConversionOperatorDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken implicitOrExplicitKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier, Microsoft.CodeAnalysis.SyntaxToken operatorKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ConversionOperatorDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.ConversionOperatorDeclarationSyntax.ExplicitInterfaceSpecifier.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ExplicitInterfaceSpecifierSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.ConversionOperatorDeclarationSyntax.Update(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken implicitOrExplicitKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier, Microsoft.CodeAnalysis.SyntaxToken operatorKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ConversionOperatorDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.ConversionOperatorDeclarationSyntax.WithExplicitInterfaceSpecifier(Microsoft.CodeAnalysis.CSharp.Syntax.ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.ConversionOperatorDeclarationSyntax diff --git a/src/Compilers/CSharp/Portable/Syntax/ConversionOperatorDeclarationSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/ConversionOperatorDeclarationSyntax.cs new file mode 100644 index 0000000000000..a59184c90ac57 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Syntax/ConversionOperatorDeclarationSyntax.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.CSharp.Syntax +{ + public partial class ConversionOperatorDeclarationSyntax + { + public ConversionOperatorDeclarationSyntax Update( + SyntaxList attributeLists, + SyntaxTokenList modifiers, + SyntaxToken implicitOrExplicitKeyword, + SyntaxToken operatorKeyword, + TypeSyntax type, + ParameterListSyntax parameterList, + BlockSyntax? body, + ArrowExpressionClauseSyntax? expressionBody, + SyntaxToken semicolonToken) + { + return Update( + attributeLists: attributeLists, + modifiers: modifiers, + implicitOrExplicitKeyword: implicitOrExplicitKeyword, + explicitInterfaceSpecifier: this.ExplicitInterfaceSpecifier, + operatorKeyword: operatorKeyword, + type: type, + parameterList: parameterList, + body: body, + expressionBody: expressionBody, + semicolonToken: semicolonToken); + } + } +} diff --git a/src/Compilers/CSharp/Portable/Syntax/OperatorDeclarationSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/OperatorDeclarationSyntax.cs index bbfb4f31c77c9..75c87324af6b2 100644 --- a/src/Compilers/CSharp/Portable/Syntax/OperatorDeclarationSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/OperatorDeclarationSyntax.cs @@ -2,10 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; - namespace Microsoft.CodeAnalysis.CSharp.Syntax { public partial class OperatorDeclarationSyntax @@ -25,7 +21,7 @@ public OperatorDeclarationSyntax Update( attributeLists: attributeLists, modifiers: modifiers, returnType: returnType, - explicitInterfaceSpecifier: null, + explicitInterfaceSpecifier: this.ExplicitInterfaceSpecifier, operatorKeyword: operatorKeyword, operatorToken: operatorToken, parameterList: parameterList, diff --git a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml index cd35f755dbd45..34589f3546ed9 100644 --- a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml +++ b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml @@ -3784,6 +3784,7 @@ + Gets the "operator" token. diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs index 771ab0d874349..055227a72e3c6 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs @@ -2468,6 +2468,49 @@ public static ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration( semicolonToken: semicolonToken); } + public static ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration( + SyntaxList attributeLists, SyntaxTokenList modifiers, + SyntaxToken implicitOrExplicitKeyword, + SyntaxToken operatorKeyword, + TypeSyntax type, + ParameterListSyntax parameterList, + BlockSyntax? body, + ArrowExpressionClauseSyntax? expressionBody, + SyntaxToken semicolonToken) + { + return SyntaxFactory.ConversionOperatorDeclaration( + attributeLists: attributeLists, + modifiers: modifiers, + implicitOrExplicitKeyword: implicitOrExplicitKeyword, + explicitInterfaceSpecifier: null, + operatorKeyword: operatorKeyword, + type: type, + parameterList: parameterList, + body: body, + expressionBody: expressionBody, + semicolonToken: semicolonToken); + } + + public static ConversionOperatorDeclarationSyntax ConversionOperatorDeclaration( + SyntaxList attributeLists, + SyntaxTokenList modifiers, + SyntaxToken implicitOrExplicitKeyword, + TypeSyntax type, + ParameterListSyntax parameterList, + BlockSyntax? body, + ArrowExpressionClauseSyntax? expressionBody) + { + return SyntaxFactory.ConversionOperatorDeclaration( + attributeLists: attributeLists, + modifiers: modifiers, + implicitOrExplicitKeyword: implicitOrExplicitKeyword, + explicitInterfaceSpecifier: null, + type: type, + parameterList: parameterList, + body: body, + expressionBody: expressionBody); + } + /// Creates a new OperatorDeclarationSyntax instance. public static OperatorDeclarationSyntax OperatorDeclaration( SyntaxList attributeLists, diff --git a/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs b/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs index eda49fcd90b40..7bd8becf61607 100644 --- a/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs +++ b/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs @@ -533,7 +533,7 @@ private static Syntax.InternalSyntax.OperatorDeclarationSyntax GenerateOperatorD => InternalSyntaxFactory.OperatorDeclaration(new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), GenerateIdentifierName(), null, InternalSyntaxFactory.Token(SyntaxKind.OperatorKeyword), InternalSyntaxFactory.Token(SyntaxKind.PlusToken), GenerateParameterList(), null, null, null); private static Syntax.InternalSyntax.ConversionOperatorDeclarationSyntax GenerateConversionOperatorDeclaration() - => InternalSyntaxFactory.ConversionOperatorDeclaration(new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), InternalSyntaxFactory.Token(SyntaxKind.ImplicitKeyword), InternalSyntaxFactory.Token(SyntaxKind.OperatorKeyword), GenerateIdentifierName(), GenerateParameterList(), null, null, null); + => InternalSyntaxFactory.ConversionOperatorDeclaration(new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), InternalSyntaxFactory.Token(SyntaxKind.ImplicitKeyword), null, InternalSyntaxFactory.Token(SyntaxKind.OperatorKeyword), GenerateIdentifierName(), GenerateParameterList(), null, null, null); private static Syntax.InternalSyntax.ConstructorDeclarationSyntax GenerateConstructorDeclaration() => InternalSyntaxFactory.ConstructorDeclaration(new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), InternalSyntaxFactory.Identifier("Identifier"), GenerateParameterList(), null, null, null, null); @@ -2914,6 +2914,7 @@ public void TestConversionOperatorDeclarationFactoryAndProperties() Assert.Equal(default, node.AttributeLists); Assert.Equal(default, node.Modifiers); Assert.Equal(SyntaxKind.ImplicitKeyword, node.ImplicitOrExplicitKeyword.Kind); + Assert.Null(node.ExplicitInterfaceSpecifier); Assert.Equal(SyntaxKind.OperatorKeyword, node.OperatorKeyword.Kind); Assert.NotNull(node.Type); Assert.NotNull(node.ParameterList); @@ -10201,7 +10202,7 @@ private static OperatorDeclarationSyntax GenerateOperatorDeclaration() => SyntaxFactory.OperatorDeclaration(new SyntaxList(), new SyntaxTokenList(), GenerateIdentifierName(), default(ExplicitInterfaceSpecifierSyntax), SyntaxFactory.Token(SyntaxKind.OperatorKeyword), SyntaxFactory.Token(SyntaxKind.PlusToken), GenerateParameterList(), default(BlockSyntax), default(ArrowExpressionClauseSyntax), default(SyntaxToken)); private static ConversionOperatorDeclarationSyntax GenerateConversionOperatorDeclaration() - => SyntaxFactory.ConversionOperatorDeclaration(new SyntaxList(), new SyntaxTokenList(), SyntaxFactory.Token(SyntaxKind.ImplicitKeyword), SyntaxFactory.Token(SyntaxKind.OperatorKeyword), GenerateIdentifierName(), GenerateParameterList(), default(BlockSyntax), default(ArrowExpressionClauseSyntax), default(SyntaxToken)); + => SyntaxFactory.ConversionOperatorDeclaration(new SyntaxList(), new SyntaxTokenList(), SyntaxFactory.Token(SyntaxKind.ImplicitKeyword), default(ExplicitInterfaceSpecifierSyntax), SyntaxFactory.Token(SyntaxKind.OperatorKeyword), GenerateIdentifierName(), GenerateParameterList(), default(BlockSyntax), default(ArrowExpressionClauseSyntax), default(SyntaxToken)); private static ConstructorDeclarationSyntax GenerateConstructorDeclaration() => SyntaxFactory.ConstructorDeclaration(new SyntaxList(), new SyntaxTokenList(), SyntaxFactory.Identifier("Identifier"), GenerateParameterList(), default(ConstructorInitializerSyntax), default(BlockSyntax), default(ArrowExpressionClauseSyntax), default(SyntaxToken)); @@ -12582,13 +12583,14 @@ public void TestConversionOperatorDeclarationFactoryAndProperties() Assert.Equal(default, node.AttributeLists); Assert.Equal(default, node.Modifiers); Assert.Equal(SyntaxKind.ImplicitKeyword, node.ImplicitOrExplicitKeyword.Kind()); + Assert.Null(node.ExplicitInterfaceSpecifier); Assert.Equal(SyntaxKind.OperatorKeyword, node.OperatorKeyword.Kind()); Assert.NotNull(node.Type); Assert.NotNull(node.ParameterList); Assert.Null(node.Body); Assert.Null(node.ExpressionBody); Assert.Equal(SyntaxKind.None, node.SemicolonToken.Kind()); - var newNode = node.WithAttributeLists(node.AttributeLists).WithModifiers(node.Modifiers).WithImplicitOrExplicitKeyword(node.ImplicitOrExplicitKeyword).WithOperatorKeyword(node.OperatorKeyword).WithType(node.Type).WithParameterList(node.ParameterList).WithBody(node.Body).WithExpressionBody(node.ExpressionBody).WithSemicolonToken(node.SemicolonToken); + var newNode = node.WithAttributeLists(node.AttributeLists).WithModifiers(node.Modifiers).WithImplicitOrExplicitKeyword(node.ImplicitOrExplicitKeyword).WithExplicitInterfaceSpecifier(node.ExplicitInterfaceSpecifier).WithOperatorKeyword(node.OperatorKeyword).WithType(node.Type).WithParameterList(node.ParameterList).WithBody(node.Body).WithExpressionBody(node.ExpressionBody).WithSemicolonToken(node.SemicolonToken); Assert.Equal(node, newNode); } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs index fd1a238900384..25d5105933468 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs @@ -2364,23 +2364,55 @@ public void OperatorDeclaration_ExplicitImplementation_21() foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { UsingDeclaration("public int I..operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), - // (1,13): error CS1003: Syntax error, ',' expected + // (1,14): error CS1001: Identifier expected // public int I..operator +(int x) => x; - Diagnostic(ErrorCode.ERR_SyntaxError, "..").WithArguments(",", "..").WithLocation(1, 13) + Diagnostic(ErrorCode.ERR_IdentifierExpected, ".operato").WithLocation(1, 14) ); - N(SyntaxKind.FieldDeclaration); + N(SyntaxKind.OperatorDeclaration); { N(SyntaxKind.PublicKeyword); - N(SyntaxKind.VariableDeclaration); + N(SyntaxKind.PredefinedType); { - N(SyntaxKind.PredefinedType); + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); { - N(SyntaxKind.IntKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } } - N(SyntaxKind.VariableDeclarator); + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); { - N(SyntaxKind.IdentifierToken, "I"); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); } } N(SyntaxKind.SemicolonToken); @@ -3890,22 +3922,54 @@ public void OperatorDeclaration_ExplicitImplementation_43() foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { UsingDeclaration("int I..operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), - // (1,6): error CS1003: Syntax error, ',' expected + // (1,7): error CS1001: Identifier expected // int I..operator +(int x) => x; - Diagnostic(ErrorCode.ERR_SyntaxError, "..").WithArguments(",", "..").WithLocation(1, 6) + Diagnostic(ErrorCode.ERR_IdentifierExpected, ".operato").WithLocation(1, 7) ); - N(SyntaxKind.FieldDeclaration); + N(SyntaxKind.OperatorDeclaration); { - N(SyntaxKind.VariableDeclaration); + N(SyntaxKind.PredefinedType); { - N(SyntaxKind.PredefinedType); + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); { - N(SyntaxKind.IntKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } } - N(SyntaxKind.VariableDeclarator); + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); { - N(SyntaxKind.IdentifierToken, "I"); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); } } N(SyntaxKind.SemicolonToken); @@ -3975,5 +4039,2585 @@ public void OperatorDeclaration_ExplicitImplementation_44() EOF(); } } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_45() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("int N.I..operator +(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,9): error CS1001: Identifier expected + // int N.I..operator +(int x) => x; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ".operato").WithLocation(1, 9) + ); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_46() + { + var errors = new[] { + // (1,5): error CS1001: Identifier expected + // N.I.operator +(int x) => x; + Diagnostic(ErrorCode.ERR_IdentifierExpected, "operator").WithLocation(1, 5) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingDeclaration("N.I.operator +(int x) => x;", options: options.WithLanguageVersion(version), errors); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + } + + [Fact] + public void OperatorDeclaration_ExplicitImplementation_47() + { + var errors = new[] { + // (1,1): error CS1073: Unexpected token 'int' + // N.I. int(int x) => x; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "N.I. ").WithArguments("int").WithLocation(1, 1), + // (1,6): error CS1001: Identifier expected + // N.I. int(int x) => x; + Diagnostic(ErrorCode.ERR_IdentifierExpected, "int").WithLocation(1, 6) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingDeclaration("N.I. int(int x) => x;", options: options.WithLanguageVersion(version), errors); + + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + EOF(); + } + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_01() + { + var error = + // (1,10): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // implicit N.I.operator int(int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I.").WithArguments("static abstract members in interfaces").WithLocation(1, 10); + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingDeclaration("implicit N.I.operator int(int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? new[] { error } : new DiagnosticDescription[] { }); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ImplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_02() + { + var errors = new[] { + // (1,1): error CS1003: Syntax error, 'explicit' expected + // N.I.operator int(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "N").WithArguments("explicit", "").WithLocation(1, 1) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingDeclaration("N.I.operator int(int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,1): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // N.I.operator int(int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I.").WithArguments("static abstract members in interfaces").WithLocation(1, 1) + ).ToArray() : + errors); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + M(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_03() + { + var errors = new[] { + // (1,1): error CS1003: Syntax error, 'explicit' expected + // operator int(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments("explicit", "operator").WithLocation(1, 1) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingDeclaration("operator int(int x) => x;", options: options.WithLanguageVersion(version), errors); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + M(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_04() + { + var errors = new[] { + // (1,14): error CS1003: Syntax error, '.' expected + // implicit N.I operator int(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".", "operator").WithLocation(1, 14) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingDeclaration("implicit N.I operator int(int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,10): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // implicit N.I operator int(int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I ").WithArguments("static abstract members in interfaces").WithLocation(1, 10) + ).ToArray() : + errors); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ImplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_05() + { + var errors = new[] { + // (1,12): error CS1003: Syntax error, '.' expected + // explicit I operator int(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".", "operator").WithLocation(1, 12) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingDeclaration("explicit I operator int(int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,10): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // explicit I operator int(int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "I ").WithArguments("static abstract members in interfaces").WithLocation(1, 10) + ).ToArray() : + errors); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_06() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("implicit N::I::operator int(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,14): error CS7000: Unexpected use of an aliased name + // implicit N::I::operator int(int x) => x; + Diagnostic(ErrorCode.ERR_UnexpectedAliasedName, "::").WithLocation(1, 14) + ); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ImplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_07() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("explicit I::operator int(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,11): error CS0687: The namespace alias qualifier '::' always resolves to a type or namespace so is illegal here. Consider using '.' instead. + // explicit I::operator int(int x) => x; + Diagnostic(ErrorCode.ERR_AliasQualAsExpression, "::").WithLocation(1, 11) + ); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_08() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("implicit I.operator int(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview)); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ImplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_09() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("explicit I.operator int(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview)); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "I"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_10() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("implicit N1::N2::I.operator int(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,16): error CS7000: Unexpected use of an aliased name + // implicit N1::N2::I.operator int(int x) => x; + Diagnostic(ErrorCode.ERR_UnexpectedAliasedName, "::").WithLocation(1, 16) + ); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ImplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N1"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N2"); + } + } + M(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_11() + { + var error = + // (1,10): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // explicit N.I.operator int(int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I.").WithArguments("static abstract members in interfaces").WithLocation(1, 10); + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingTree("explicit N.I.operator int(int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? new[] { error } : new DiagnosticDescription[] { }); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_12() + { + var errors = new[] { + // (1,14): error CS1003: Syntax error, '.' expected + // implicit N.I int(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(".", "int").WithLocation(1, 14), + // (1,14): error CS1003: Syntax error, 'operator' expected + // implicit N.I int(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments("operator", "int").WithLocation(1, 14) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingTree("implicit N.I int(int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,10): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // implicit N.I int(int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I ").WithArguments("static abstract members in interfaces").WithLocation(1, 10) + ).ToArray() : + errors); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ImplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + M(SyntaxKind.DotToken); + } + M(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_13() + { + var errors = new[] { + // (1,15): error CS1003: Syntax error, 'operator' expected + // explicit N.I. int(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments("operator", "int").WithLocation(1, 15) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingTree("explicit N.I. int(int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,10): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // explicit N.I. int(int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I.").WithArguments("static abstract members in interfaces").WithLocation(1, 10) + ).ToArray() : + errors); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + M(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_14() + { + var errors = new[] { + // (1,14): error CS1003: Syntax error, '.' expected + // implicit N.I operator int(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".", "operator").WithLocation(1, 14) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingTree("implicit N.I operator int(int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,10): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // implicit N.I operator int(int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "N.I ").WithArguments("static abstract members in interfaces").WithLocation(1, 10) + ).ToArray() : + errors); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ImplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_15() + { + var errors = new[] { + // (1,12): error CS1003: Syntax error, '.' expected + // explicit I operator int(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".", "operator").WithLocation(1, 12) + }; + + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) + { + UsingTree("explicit I operator int(int x) => x;", options: options.WithLanguageVersion(version), + version == LanguageVersion.CSharp9 ? + errors.Append( + // (1,10): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // explicit I operator int(int x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "I ").WithArguments("static abstract members in interfaces").WithLocation(1, 10) + ).ToArray() : + errors); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_16() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingTree("implicit N::I::operator int(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,14): error CS7000: Unexpected use of an aliased name + // implicit N::I::operator int(int x) => x; + Diagnostic(ErrorCode.ERR_UnexpectedAliasedName, "::").WithLocation(1, 14) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ImplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_17() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingTree("explicit I::operator int(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,11): error CS0687: The namespace alias qualifier '::' always resolves to a type or namespace so is illegal here. Consider using '.' instead. + // explicit I::operator int(int x) => x; + Diagnostic(ErrorCode.ERR_AliasQualAsExpression, "::").WithLocation(1, 11) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_18() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingTree("implicit I.operator int(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ImplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_19() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingTree("explicit I.operator int(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "I"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_20() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingTree("implicit N1::N2::I.operator int(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,16): error CS7000: Unexpected use of an aliased name + // implicit N1::N2::I.operator int(int x) => x; + Diagnostic(ErrorCode.ERR_UnexpectedAliasedName, "::").WithLocation(1, 16) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ImplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N1"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N2"); + } + } + M(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_21() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("explicit I..operator int(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,12): error CS1001: Identifier expected + // explicit I..operator int(int x) => x; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ".operato").WithLocation(1, 12) + ); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_22() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("implicit I . . operator int(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,14): error CS1001: Identifier expected + // implicit I . . operator int(int x) => x; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ".").WithLocation(1, 14) + ); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ImplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_23() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("explicit I T(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,12): error CS1003: Syntax error, '.' expected + // explicit I T(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "T").WithArguments(".", "").WithLocation(1, 12), + // (1,12): error CS1003: Syntax error, 'operator' expected + // explicit I T(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "T").WithArguments("operator", "").WithLocation(1, 12) + ); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + M(SyntaxKind.DotToken); + } + M(SyntaxKind.OperatorKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_24() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("explicit I.T(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,12): error CS1003: Syntax error, 'operator' expected + // explicit I.T(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "T").WithArguments("operator", "").WithLocation(1, 12) + ); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + } + M(SyntaxKind.OperatorKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_25() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("explicit I.operator (int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,21): error CS1001: Identifier expected + // explicit I.operator (int x) => x; + Diagnostic(ErrorCode.ERR_IdentifierExpected, "(").WithLocation(1, 21) + ); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_26() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("explicit I.operator (int x) { return x; }", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,21): error CS1001: Identifier expected + // explicit I.operator (int x) { return x; } + Diagnostic(ErrorCode.ERR_IdentifierExpected, "(").WithLocation(1, 21) + ); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ReturnStatement); + { + N(SyntaxKind.ReturnKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_27() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("explicit I.operator (int x);", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,21): error CS1001: Identifier expected + // explicit I.operator (int x); + Diagnostic(ErrorCode.ERR_IdentifierExpected, "(").WithLocation(1, 21) + ); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_28() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("explicit I.T1 T2(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,15): error CS1003: Syntax error, '.' expected + // explicit I.T1 T2(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "T2").WithArguments(".", "").WithLocation(1, 15), + // (1,15): error CS1003: Syntax error, 'operator' expected + // explicit I.T1 T2(int x) => x; + Diagnostic(ErrorCode.ERR_SyntaxError, "T2").WithArguments("operator", "").WithLocation(1, 15) + ); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T1"); + } + } + M(SyntaxKind.DotToken); + } + M(SyntaxKind.OperatorKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T2"); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_29() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("explicit I.operator (int x)", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,21): error CS1001: Identifier expected + // explicit I.operator (int x) + Diagnostic(ErrorCode.ERR_IdentifierExpected, "(").WithLocation(1, 21), + // (1,28): error CS1002: ; expected + // explicit I.operator (int x) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 28) + ); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_30() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("explicit I.operator (int x, );", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,29): error CS1031: Type expected + // explicit I.operator (int x, ); + Diagnostic(ErrorCode.ERR_TypeExpected, ")").WithLocation(1, 29), + // (1,30): error CS1003: Syntax error, '(' expected + // explicit I.operator (int x, ); + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("(", ";").WithLocation(1, 30), + // (1,30): error CS1026: ) expected + // explicit I.operator (int x, ); + Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(1, 30) + ); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.TupleElement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.ParameterList); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_31() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("explicit I.operator (int x, int y);", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,35): error CS1003: Syntax error, '(' expected + // explicit I.operator (int x, int y); + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("(", ";").WithLocation(1, 35), + // (1,35): error CS1026: ) expected + // explicit I.operator (int x, int y); + Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(1, 35) + ); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.ParameterList); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_32() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("explicit I.operator var(x);", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,26): error CS1001: Identifier expected + // explicit I.operator var(x); + Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(1, 26) + ); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_33() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("explicit I.operator (int x int y);", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,21): error CS1001: Identifier expected + // explicit I.operator (int x int y); + Diagnostic(ErrorCode.ERR_IdentifierExpected, "(").WithLocation(1, 21), + // (1,28): error CS1003: Syntax error, ',' expected + // explicit I.operator (int x int y); + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(",", "int").WithLocation(1, 28) + ); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_34() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + UsingDeclaration("explicit N.I..operator int(int x) => x;", options: options.WithLanguageVersion(LanguageVersion.Preview), + // (1,14): error CS1001: Identifier expected + // explicit N.I..operator int(int x) => x; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ".operato").WithLocation(1, 14) + ); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "I"); + } + } + N(SyntaxKind.DotToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + public void ConversionDeclaration_ExplicitImplementation_35() + { + var error = new[] { + // (2,9): error CS1003: Syntax error, 'operator' expected + // explicit + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("operator", "").WithLocation(2, 9), + // (2,9): error CS1001: Identifier expected + // explicit + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(2, 9) + }; + + UsingTree( +@" +explicit +Func f1 = (param1) => 10; +", options: TestOptions.Regular, error); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.ExplicitKeyword); + M(SyntaxKind.OperatorKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.ParameterList); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Func"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "f1"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "param1"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "10"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void DotDotRecovery_01() + { + UsingDeclaration("N1..N2 M(int x) => x;", options: TestOptions.Regular, + // (1,4): error CS1001: Identifier expected + // N1..N2 M(int x) => x; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ".").WithLocation(1, 4) + ); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N1"); + } + N(SyntaxKind.DotToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N2"); + } + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void DotDotRecovery_02() + { + UsingDeclaration("int N1..M(int x) => x;", options: TestOptions.Regular, + // (1,8): error CS1001: Identifier expected + // int N1..M(int x) => x; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ".").WithLocation(1, 8) + ); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N1"); + } + N(SyntaxKind.DotToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void DotDotRecovery_03() + { + UsingDeclaration("int N1.N2..M(int x) => x;", options: TestOptions.Regular, + // (1,11): error CS1001: Identifier expected + // int N1.N2..M(int x) => x; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ".").WithLocation(1, 11) + ); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N1"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "N2"); + } + } + N(SyntaxKind.DotToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.DotToken); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs index 6d651a3a88adc..9710e38fb52e1 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs @@ -2886,12 +2886,9 @@ public static A operator () // (4,43): error CS1003: Syntax error, ',' expected // public static int explicit operator () Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",", "{").WithLocation(4, 43), - // (6,18): error CS1026: ) expected + // (6,17): error CS1026: ) expected // return 0; - Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(6, 18), - // (6,18): error CS1002: ; expected - // return 0; - Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(6, 18), + Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(6, 17), // (8,30): error CS1037: Overloadable operator expected // public static A operator () Diagnostic(ErrorCode.ERR_OvlOperatorExpected, "(").WithLocation(8, 30), @@ -2947,12 +2944,9 @@ public static A operator () // (4,43): error CS1003: Syntax error, ',' expected // public static int explicit operator () Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",", "{").WithLocation(4, 43), - // (6,18): error CS1026: ) expected - // return 0; - Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(6, 18), - // (6,18): error CS1002: ; expected + // (6,17): error CS1026: ) expected // return 0; - Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(6, 18), + Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(6, 17), // (8,30): error CS1037: Overloadable operator expected // public static A operator () Diagnostic(ErrorCode.ERR_OvlOperatorExpected, "(").WithLocation(8, 30), @@ -4650,12 +4644,9 @@ public static int Main () // (3,49): error CS1003: Syntax error, ',' expected // public static int implicit operator (goo f) { return 6; } // Error Diagnostic(ErrorCode.ERR_SyntaxError, "{").WithArguments(",", "{").WithLocation(3, 49), - // (3,61): error CS1026: ) expected + // (3,59): error CS1026: ) expected // public static int implicit operator (goo f) { return 6; } // Error - Diagnostic(ErrorCode.ERR_CloseParenExpected, "}").WithLocation(3, 61), - // (3,61): error CS1002: ; expected - // public static int implicit operator (goo f) { return 6; } // Error - Diagnostic(ErrorCode.ERR_SemicolonExpected, "}").WithLocation(3, 61), + Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(3, 59), // (4,1): error CS1022: Type or namespace definition, or end-of-file expected // } Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(4, 1) @@ -4707,12 +4698,9 @@ public static int Main () // (3,49): error CS1003: Syntax error, ',' expected // public static int implicit operator (goo f) { return 6; } // Error Diagnostic(ErrorCode.ERR_SyntaxError, "{").WithArguments(",", "{").WithLocation(3, 49), - // (3,61): error CS1026: ) expected - // public static int implicit operator (goo f) { return 6; } // Error - Diagnostic(ErrorCode.ERR_CloseParenExpected, "}").WithLocation(3, 61), - // (3,61): error CS1002: ; expected + // (3,59): error CS1026: ) expected // public static int implicit operator (goo f) { return 6; } // Error - Diagnostic(ErrorCode.ERR_SemicolonExpected, "}").WithLocation(3, 61), + Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(3, 59), // (4,1): error CS1022: Type or namespace definition, or end-of-file expected // } Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(4, 1) diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ScriptParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ScriptParsingTests.cs index 93d5b99c954fb..c1655b314028c 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ScriptParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ScriptParsingTests.cs @@ -1680,8 +1680,9 @@ public void Error_IndexerDefinition() ParseAndValidate(test, new ErrorDescription { Code = 1001, Line = 1, Column = 13 }, new ErrorDescription { Code = 1003, Line = 1, Column = 13 }, - new ErrorDescription { Code = 1003, Line = 1, Column = 17 }, - new ErrorDescription { Code = 1514, Line = 1, Column = 17 }, + new ErrorDescription { Code = 1003, Line = 1, Column = 16 }, + new ErrorDescription { Code = 1514, Line = 1, Column = 16 }, + new ErrorDescription { Code = 1014, Line = 1, Column = 16 }, new ErrorDescription { Code = 1513, Line = 1, Column = 17 }); CreateCompilation(test).VerifyDiagnostics( @@ -1691,12 +1692,15 @@ public void Error_IndexerDefinition() // (1,13): error CS1001: Identifier expected // string this =""; Diagnostic(ErrorCode.ERR_IdentifierExpected, "=").WithLocation(1, 13), - // (1,17): error CS1003: Syntax error, ']' expected + // (1,16): error CS1003: Syntax error, ']' expected // string this =""; - Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("]", "").WithLocation(1, 17), - // (1,17): error CS1514: { expected + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("]", ";").WithLocation(1, 16), + // (1,16): error CS1514: { expected // string this =""; - Diagnostic(ErrorCode.ERR_LbraceExpected, "").WithLocation(1, 17), + Diagnostic(ErrorCode.ERR_LbraceExpected, ";").WithLocation(1, 16), + // (1,16): error CS1014: A get or set accessor expected + // string this =""; + Diagnostic(ErrorCode.ERR_GetOrSetExpected, ";").WithLocation(1, 16), // (1,17): error CS1513: } expected // string this =""; Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(1, 17), From eab3d805f078899e44232d824b42d0f7ae43c9fd Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Thu, 20 May 2021 15:42:09 -0700 Subject: [PATCH 085/127] Post merge fixups --- .../ValueTrackedTreeItemViewModel.cs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs index dfe388753dcbc..af14262cd990d 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.ValueTracking; using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.VisualStudio.Shell; namespace Microsoft.VisualStudio.LanguageServices.ValueTracking @@ -95,21 +96,15 @@ private void CalculateChildren() { try { - var items = await _valueTrackingService.TrackValueSourceAsync(Workspace.CurrentSolution, _trackedItem, ThreadingContext.DisposalToken).ConfigureAwait(false); + var children = await CalculateChildrenAsync(ThreadingContext.DisposalToken).ConfigureAwait(false); await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(); ChildItems.Clear(); - foreach (var valueTrackedItem in items) + foreach (var child in children) { - ChildItems.Add(new ValueTrackedTreeItemViewModel( - valueTrackedItem, - _solution, - TreeViewModel, - _glyphService, - _valueTrackingService, - ThreadingContext)); + ChildItems.Add(child); } } finally @@ -134,7 +129,7 @@ public override void Select() var options = Workspace.Options .WithChangedOption(new OptionKey(NavigationOptions.PreferProvisionalTab), true) .WithChangedOption(new OptionKey(NavigationOptions.ActivateTab), false); - + navigationService.TryNavigateToSpan(Workspace, DocumentId, _trackedItem.Span, options, ThreadingContext.DisposalToken); } @@ -145,8 +140,8 @@ private async Task> CalculateChildrenAsync(Can _trackedItem, cancellationToken).ConfigureAwait(false); - // TODO: Use pooled item here? - var builder = new List(); + var builder = ImmutableArray.CreateBuilder(valueTrackedItems.Length); + foreach (var valueTrackedItem in valueTrackedItems) { var document = _solution.GetRequiredDocument(valueTrackedItem.DocumentId); From 766afd9447bc656cafc85f5fef1f1c33c10abd8d Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Thu, 20 May 2021 17:43:21 -0700 Subject: [PATCH 086/127] Add support for implementations of interface static abstract user-definedo conversions in classes and structures (#53529) --- .../BinderFactory.BinderFactoryVisitor.cs | 5 +- .../Semantics/Operators/OperatorFacts.cs | 16 + .../CSharp/Portable/CSharpResources.resx | 2 +- .../Compilation/SyntaxTreeSemanticModel.cs | 8 +- .../Declarations/DeclarationTreeBuilder.cs | 36 +- .../SymbolDisplayVisitor.Members.cs | 56 +- .../Source/SourceMemberContainerSymbol.cs | 2 +- .../SourceUserDefinedConversionSymbol.cs | 24 +- .../SourceUserDefinedOperatorSymbolBase.cs | 45 +- .../Portable/xlf/CSharpResources.cs.xlf | 4 +- .../Portable/xlf/CSharpResources.de.xlf | 4 +- .../Portable/xlf/CSharpResources.es.xlf | 4 +- .../Portable/xlf/CSharpResources.fr.xlf | 4 +- .../Portable/xlf/CSharpResources.it.xlf | 4 +- .../Portable/xlf/CSharpResources.ja.xlf | 4 +- .../Portable/xlf/CSharpResources.ko.xlf | 4 +- .../Portable/xlf/CSharpResources.pl.xlf | 4 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 4 +- .../Portable/xlf/CSharpResources.ru.xlf | 4 +- .../Portable/xlf/CSharpResources.tr.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 4 +- .../StaticAbstractMembersInInterfacesTests.cs | 1622 ++++++++++++++++- 23 files changed, 1770 insertions(+), 98 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs index 062cb1cf08e3c..5671f4ae946e0 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs @@ -451,9 +451,8 @@ private static string GetMethodName(BaseMethodDeclarationSyntax baseMethodDeclar var operatorDeclaration = (OperatorDeclarationSyntax)baseMethodDeclarationSyntax; return ExplicitInterfaceHelpers.GetMemberName(outerBinder, operatorDeclaration.ExplicitInterfaceSpecifier, OperatorFacts.OperatorNameFromDeclaration(operatorDeclaration)); case SyntaxKind.ConversionOperatorDeclaration: - return ((ConversionOperatorDeclarationSyntax)baseMethodDeclarationSyntax).ImplicitOrExplicitKeyword.Kind() == SyntaxKind.ImplicitKeyword - ? WellKnownMemberNames.ImplicitConversionName - : WellKnownMemberNames.ExplicitConversionName; + var conversionDeclaration = (ConversionOperatorDeclarationSyntax)baseMethodDeclarationSyntax; + return ExplicitInterfaceHelpers.GetMemberName(outerBinder, conversionDeclaration.ExplicitInterfaceSpecifier, OperatorFacts.OperatorNameFromDeclaration(conversionDeclaration)); case SyntaxKind.MethodDeclaration: MethodDeclarationSyntax methodDeclSyntax = (MethodDeclarationSyntax)baseMethodDeclarationSyntax; return ExplicitInterfaceHelpers.GetMemberName(outerBinder, methodDeclSyntax.ExplicitInterfaceSpecifier, methodDeclSyntax.Identifier.ValueText); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorFacts.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorFacts.cs index c79253e2961bd..870595ae6028c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorFacts.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorFacts.cs @@ -145,6 +145,22 @@ public static string OperatorNameFromDeclaration(Syntax.InternalSyntax.OperatorD } } + public static string OperatorNameFromDeclaration(ConversionOperatorDeclarationSyntax declaration) + { + return OperatorNameFromDeclaration((Syntax.InternalSyntax.ConversionOperatorDeclarationSyntax)(declaration.Green)); + } + + public static string OperatorNameFromDeclaration(Syntax.InternalSyntax.ConversionOperatorDeclarationSyntax declaration) + { + switch (declaration.ImplicitOrExplicitKeyword.Kind) + { + case SyntaxKind.ImplicitKeyword: + return WellKnownMemberNames.ImplicitConversionName; + default: + return WellKnownMemberNames.ExplicitConversionName; + } + } + public static string UnaryOperatorNameFromOperatorKind(UnaryOperatorKind kind) { switch (kind & UnaryOperatorKind.OpMask) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 02add8601a334..ca6b6590186c0 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6680,6 +6680,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Explicit implementation of a user-defined operator '{0}' must be declared static - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index 91390defe2139..7177c2780e9dd 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -1709,13 +1709,9 @@ private string GetDeclarationName(CSharpSyntaxNode declaration) } case SyntaxKind.ConversionOperatorDeclaration: - if (((ConversionOperatorDeclarationSyntax)declaration).ImplicitOrExplicitKeyword.Kind() == SyntaxKind.ExplicitKeyword) { - return WellKnownMemberNames.ExplicitConversionName; - } - else - { - return WellKnownMemberNames.ImplicitConversionName; + var operatorDecl = (ConversionOperatorDeclarationSyntax)declaration; + return GetDeclarationName(declaration, operatorDecl.ExplicitInterfaceSpecifier, OperatorFacts.OperatorNameFromDeclaration(operatorDecl)); } case SyntaxKind.EventFieldDeclaration: diff --git a/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs b/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs index b323a63a71f54..5209282f44740 100644 --- a/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs +++ b/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs @@ -784,27 +784,33 @@ private static void AddNonTypeMemberNames( break; case SyntaxKind.OperatorDeclaration: - anyNonTypeMembers = true; + { + anyNonTypeMembers = true; - // Member names are exposed via NamedTypeSymbol.MemberNames and are used primarily - // as an acid test to determine whether a more in-depth search of a type is worthwhile. - // We decided that it was reasonable to exclude explicit interface implementations - // from the list of member names. - var opDecl = (Syntax.InternalSyntax.OperatorDeclarationSyntax)member; + // Handle in the same way as explicit method implementations + var opDecl = (Syntax.InternalSyntax.OperatorDeclarationSyntax)member; - if (opDecl.ExplicitInterfaceSpecifier == null) - { - var name = OperatorFacts.OperatorNameFromDeclaration(opDecl); - set.Add(name); + if (opDecl.ExplicitInterfaceSpecifier == null) + { + var name = OperatorFacts.OperatorNameFromDeclaration(opDecl); + set.Add(name); + } } - break; case SyntaxKind.ConversionOperatorDeclaration: - anyNonTypeMembers = true; - set.Add(((Syntax.InternalSyntax.ConversionOperatorDeclarationSyntax)member).ImplicitOrExplicitKeyword.Kind == SyntaxKind.ImplicitKeyword - ? WellKnownMemberNames.ImplicitConversionName - : WellKnownMemberNames.ExplicitConversionName); + { + anyNonTypeMembers = true; + + // Handle in the same way as explicit method implementations + var opDecl = (Syntax.InternalSyntax.ConversionOperatorDeclarationSyntax)member; + + if (opDecl.ExplicitInterfaceSpecifier == null) + { + var name = OperatorFacts.OperatorNameFromDeclaration(opDecl); + set.Add(name); + } + } break; case SyntaxKind.GlobalStatement: diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs index 0264bdd7f5136..5b806987fc8cc 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs @@ -479,7 +479,7 @@ public override void VisitMethod(IMethodSymbol symbol) AddExplicitInterfaceIfRequired(symbol.ExplicitInterfaceImplementations); if (!format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) && - symbol.GetSymbol()?.OriginalDefinition is SourceUserDefinedOperatorSymbolBase) + symbol.GetSymbol()?.OriginalDefinition is SourceUserDefinedOperatorSymbolBase sourceUserDefinedOperatorSymbolBase) { var operatorName = symbol.MetadataName; var lastDotPosition = operatorName.LastIndexOf('.'); @@ -489,7 +489,14 @@ public override void VisitMethod(IMethodSymbol symbol) operatorName = operatorName.Substring(lastDotPosition + 1); } - addUserDefinedOperatorName(symbol, operatorName); + if (sourceUserDefinedOperatorSymbolBase is SourceUserDefinedConversionSymbol) + { + addUserDefinedConversionName(symbol, operatorName); + } + else + { + addUserDefinedOperatorName(symbol, operatorName); + } break; } @@ -518,26 +525,7 @@ public override void VisitMethod(IMethodSymbol symbol) } else { - // "System.IntPtr.explicit operator System.IntPtr(int)" - - if (symbol.MetadataName == WellKnownMemberNames.ExplicitConversionName) - { - AddKeyword(SyntaxKind.ExplicitKeyword); - } - else if (symbol.MetadataName == WellKnownMemberNames.ImplicitConversionName) - { - AddKeyword(SyntaxKind.ImplicitKeyword); - } - else - { - builder.Add(CreatePart(SymbolDisplayPartKind.MethodName, symbol, - SyntaxFacts.GetText(SyntaxFacts.GetOperatorKind(symbol.MetadataName)))); - } - - AddSpace(); - AddKeyword(SyntaxKind.OperatorKeyword); - AddSpace(); - AddReturnType(symbol); + addUserDefinedConversionName(symbol, symbol.MetadataName); } break; } @@ -660,6 +648,30 @@ void addUserDefinedOperatorName(IMethodSymbol symbol, string operatorName) SyntaxFacts.GetText(SyntaxFacts.GetOperatorKind(operatorName)))); } } + + void addUserDefinedConversionName(IMethodSymbol symbol, string operatorName) + { + // "System.IntPtr.explicit operator System.IntPtr(int)" + + if (operatorName == WellKnownMemberNames.ExplicitConversionName) + { + AddKeyword(SyntaxKind.ExplicitKeyword); + } + else if (operatorName == WellKnownMemberNames.ImplicitConversionName) + { + AddKeyword(SyntaxKind.ImplicitKeyword); + } + else + { + builder.Add(CreatePart(SymbolDisplayPartKind.MethodName, symbol, + SyntaxFacts.GetText(SyntaxFacts.GetOperatorKind(operatorName)))); + } + + AddSpace(); + AddKeyword(SyntaxKind.OperatorKeyword); + AddSpace(); + AddReturnType(symbol); + } } private static SymbolDisplayPartKind GetPartKindForConstructorOrDestructor(IMethodSymbol symbol) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index f1ef1cbbbf131..22e47c3477375 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -4412,7 +4412,7 @@ private void AddNonTypeMembers( } var method = SourceUserDefinedConversionSymbol.CreateUserDefinedConversionSymbol( - this, conversionOperatorSyntax, compilation.IsNullableAnalysisEnabledIn(conversionOperatorSyntax), diagnostics); + this, bodyBinder, conversionOperatorSyntax, compilation.IsNullableAnalysisEnabledIn(conversionOperatorSyntax), diagnostics); builder.NonTypeMembers.Add(method); } break; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs index 26b1c417d7cae..f5b8502f29705 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs @@ -15,6 +15,7 @@ internal sealed class SourceUserDefinedConversionSymbol : SourceUserDefinedOpera { public static SourceUserDefinedConversionSymbol CreateUserDefinedConversionSymbol( SourceMemberContainerTypeSymbol containingType, + Binder bodyBinder, ConversionOperatorDeclarationSyntax syntax, bool isNullableAnalysisEnabled, BindingDiagnosticBag diagnostics) @@ -22,32 +23,41 @@ public static SourceUserDefinedConversionSymbol CreateUserDefinedConversionSymbo // Dev11 includes the explicit/implicit keyword, but we don't have a good way to include // Narrowing/Widening in VB and we want the languages to be consistent. var location = syntax.Type.Location; - string name = syntax.ImplicitOrExplicitKeyword.IsKind(SyntaxKind.ImplicitKeyword) - ? WellKnownMemberNames.ImplicitConversionName - : WellKnownMemberNames.ExplicitConversionName; + string name = OperatorFacts.OperatorNameFromDeclaration(syntax); + + var interfaceSpecifier = syntax.ExplicitInterfaceSpecifier; + + TypeSymbol explicitInterfaceType; + name = ExplicitInterfaceHelpers.GetMemberNameAndInterfaceSymbol(bodyBinder, interfaceSpecifier, name, diagnostics, out explicitInterfaceType, aliasQualifierOpt: out _); + + var methodKind = interfaceSpecifier == null + ? MethodKind.Conversion + : MethodKind.ExplicitInterfaceImplementation; return new SourceUserDefinedConversionSymbol( - containingType, name, location, syntax, isNullableAnalysisEnabled, diagnostics); + methodKind, containingType, explicitInterfaceType, name, location, syntax, isNullableAnalysisEnabled, diagnostics); } // NOTE: no need to call WithUnsafeRegionIfNecessary, since the signature // is bound lazily using binders from a BinderFactory (which will already include an // UnsafeBinder, if necessary). private SourceUserDefinedConversionSymbol( + MethodKind methodKind, SourceMemberContainerTypeSymbol containingType, + TypeSymbol explicitInterfaceType, string name, Location location, ConversionOperatorDeclarationSyntax syntax, bool isNullableAnalysisEnabled, BindingDiagnosticBag diagnostics) : base( - MethodKind.Conversion, - explicitInterfaceType: null, + methodKind, + explicitInterfaceType, name, containingType, location, syntax, - MakeDeclarationModifiers(MethodKind.Conversion, containingType.IsInterface, syntax, location, diagnostics), + MakeDeclarationModifiers(methodKind, containingType.IsInterface, syntax, location, diagnostics), hasBody: syntax.HasAnyBody(), isExpressionBodied: syntax.Body == null && syntax.ExpressionBody != null, isIterator: SyntaxFacts.HasYieldOperations(syntax.Body), diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs index 5929f7ddb6da5..37ac564c1aae2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs @@ -8,6 +8,7 @@ using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols { @@ -269,8 +270,26 @@ protected sealed override MethodSymbol FindExplicitlyImplementedMethod(BindingDi { if (_explicitInterfaceType is object) { - var syntax = (OperatorDeclarationSyntax)syntaxReferenceOpt.GetSyntax(); - return this.FindExplicitlyImplementedMethod(isOperator: true, _explicitInterfaceType, OperatorFacts.OperatorNameFromDeclaration(syntax), syntax.ExplicitInterfaceSpecifier, diagnostics); + string interfaceMethodName; + ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier; + + switch (syntaxReferenceOpt.GetSyntax()) + { + case OperatorDeclarationSyntax operatorDeclaration: + interfaceMethodName = OperatorFacts.OperatorNameFromDeclaration(operatorDeclaration); + explicitInterfaceSpecifier = operatorDeclaration.ExplicitInterfaceSpecifier; + break; + + case ConversionOperatorDeclarationSyntax conversionDeclaration: + interfaceMethodName = OperatorFacts.OperatorNameFromDeclaration(conversionDeclaration); + explicitInterfaceSpecifier = conversionDeclaration.ExplicitInterfaceSpecifier; + break; + + default: + throw ExceptionUtilities.Unreachable; + } + + return this.FindExplicitlyImplementedMethod(isOperator: true, _explicitInterfaceType, interfaceMethodName, explicitInterfaceSpecifier, diagnostics); } return null; @@ -745,9 +764,25 @@ protected sealed override void CheckConstraintsForExplicitInterfaceType(Conversi { if ((object)_explicitInterfaceType != null) { - var syntax = (OperatorDeclarationSyntax)syntaxReferenceOpt.GetSyntax(); - Debug.Assert(syntax.ExplicitInterfaceSpecifier != null); - _explicitInterfaceType.CheckAllConstraints(DeclaringCompilation, conversions, new SourceLocation(syntax.ExplicitInterfaceSpecifier.Name), diagnostics); + NameSyntax name; + + switch (syntaxReferenceOpt.GetSyntax()) + { + case OperatorDeclarationSyntax operatorDeclaration: + Debug.Assert(operatorDeclaration.ExplicitInterfaceSpecifier != null); + name = operatorDeclaration.ExplicitInterfaceSpecifier.Name; + break; + + case ConversionOperatorDeclarationSyntax conversionDeclaration: + Debug.Assert(conversionDeclaration.ExplicitInterfaceSpecifier != null); + name = conversionDeclaration.ExplicitInterfaceSpecifier.Name; + break; + + default: + throw ExceptionUtilities.Unreachable; + } + + _explicitInterfaceType.CheckAllConstraints(DeclaringCompilation, conversions, new SourceLocation(name), diagnostics); } } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 3ea58f2cb36c3..7c63460987ef3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -23,8 +23,8 @@ - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index baa6ce15bc573..123e9c112332c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -23,8 +23,8 @@ - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index d32e66bc7375e..b5718f7a6d7b9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -23,8 +23,8 @@ - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index f073d0d0818cd..b075d365a0d41 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -23,8 +23,8 @@ - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 33c5e1b47ff2a..7f24998af2b58 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -23,8 +23,8 @@ - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 51208f000dced..d5d38e180fa8f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -23,8 +23,8 @@ - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 4308b96fcb749..327548412db2a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -23,8 +23,8 @@ - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 96d1b05da4775..020c7cd24f6b1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -23,8 +23,8 @@ - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 3de0df26cc89c..e7e688745ce38 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -23,8 +23,8 @@ - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 4b0eaaf0f5a47..e8a967e8c2022 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -23,8 +23,8 @@ - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 6f25c40717652..fcf9d071c55e0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -23,8 +23,8 @@ - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 85b4361c5209d..27d3d8dba91ed 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -23,8 +23,8 @@ - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 2d8f3e802f49d..6e3a7b6334185 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -23,8 +23,8 @@ - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type - User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index f6e7e704aa139..1870bcf8fb4f5 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -5972,22 +5972,22 @@ interface I19 where T19_1 : I19, T19_2 // (9,39): error CS1964: 'I2.explicit operator dynamic(T2)': user-defined conversions to or from the dynamic type are not allowed // abstract static explicit operator dynamic(T2 y); Diagnostic(ErrorCode.ERR_BadDynamicConversion, "dynamic").WithArguments("I2." + op + " operator dynamic(T2)").WithLocation(9, 39), - // (26,43): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // (26,43): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator T5 (bool y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "T5").WithLocation(26, 43), - // (32,39): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // (32,39): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator T71 (bool y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "T71").WithLocation(32, 39), - // (37,39): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // (37,39): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator T8(bool y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "T8").WithLocation(37, 39), - // (44,39): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // (44,39): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator T10(bool y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "T10").WithLocation(44, 39), // (47,18): error CS0535: 'C11' does not implement interface member 'I10.explicit operator T11(bool)' // class C11 : I10 where T11 : C11 {} Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I10").WithArguments("C11", "I10." + op + " operator T11(bool)").WithLocation(47, 18), - // (51,39): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // (51,39): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator int(bool y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "int").WithLocation(51, 39), // (56,39): error CS0552: 'I13.explicit operator I13(bool)': user-defined conversions to or from an interface are not allowed @@ -6093,22 +6093,22 @@ interface I19 where T19_1 : I19, T19_2 // (9,39): error CS1964: 'I2.explicit operator T2(dynamic)': user-defined conversions to or from the dynamic type are not allowed // abstract static explicit operator T2(dynamic y); Diagnostic(ErrorCode.ERR_BadDynamicConversion, "T2").WithArguments("I2." + op + " operator T2(dynamic)").WithLocation(9, 39), - // (26,43): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // (26,43): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator bool(T5 y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "bool").WithLocation(26, 43), - // (32,39): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // (32,39): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator bool(T71 y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "bool").WithLocation(32, 39), - // (37,39): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // (37,39): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator bool(T8 y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "bool").WithLocation(37, 39), - // (44,39): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // (44,39): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator bool(T10 y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "bool").WithLocation(44, 39), // (47,18): error CS0535: 'C11' does not implement interface member 'I10.explicit operator bool(T11)' // class C11 : I10 where T11 : C11 {} Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I10").WithArguments("C11", "I10." + op + " operator bool(T11)").WithLocation(47, 18), - // (51,39): error CS9112: User-defined conversion in an interface must convert to or from the enclosing type type parameter constrained to the enclosing type + // (51,39): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator bool(int y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "bool").WithLocation(51, 39), // (56,39): error CS0552: 'I13.explicit operator bool(I13)': user-defined conversions to or from an interface are not allowed @@ -17258,7 +17258,7 @@ class C2 : I1 } "; - var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll.WithAllowUnsafe(true), + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, parseOptions: TestOptions.RegularPreview, targetFramework: TargetFramework.NetCoreApp); @@ -17303,7 +17303,7 @@ struct C2 : I1 } "; - var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll.WithAllowUnsafe(true), + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, parseOptions: TestOptions.RegularPreview, targetFramework: TargetFramework.NetCoreApp); @@ -22386,5 +22386,1603 @@ void validate(ModuleSymbol module) Assert.Same(c1M01, c2.BaseType().FindImplementationForInterfaceMember(m01)); } } + + private static string ConversionOperatorName(string op) => op switch { "implicit" => WellKnownMemberNames.ImplicitConversionName, "explicit" => WellKnownMemberNames.ExplicitConversionName, _ => throw TestExceptionUtilities.UnexpectedValue(op) }; + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticConversionOperator_01([CombinatorialValues("implicit", "explicit")] string op, bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + string opName = ConversionOperatorName(op); + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator int(T x); +} + +" + typeKeyword + @" + C1 : I1 +{} + +" + typeKeyword + @" + C2 : I1 +{ + public " + op + @" operator int(C2 x) => throw null; +} + +" + typeKeyword + @" + C3 : I1 +{ + static " + op + @" operator int(C3 x) => throw null; +} + +" + typeKeyword + @" + C4 : I1 +{ + " + op + @" I1.operator int(C4 x) => throw null; +} + +" + typeKeyword + @" + C5 : I1 +{ + public static " + op + @" operator long(C5 x) => throw null; +} + +" + typeKeyword + @" + C6 : I1 +{ + static " + op + @" I1.operator long(C6 x) => throw null; +} + +" + typeKeyword + @" + C7 : I1 +{ + public static int " + opName + @"(C7 x) => throw null; +} + +" + typeKeyword + @" + C8 : I1 +{ + static int I1." + opName + @"(C8 x) => throw null; +} + +public interface I2 where T : I2 +{ + abstract static int " + opName + @"(T x); +} + +" + typeKeyword + @" + C9 : I2 +{ + public static " + op + @" operator int(C9 x) => throw null; +} + +" + typeKeyword + @" + C10 : I2 +{ + static " + op + @" I2.operator int(C10 x) => throw null; +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (8,10): error CS0535: 'C1' does not implement interface member 'I1.explicit operator int(C1)' + // C1 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1." + op + " operator int(C1)").WithLocation(8, 10), + // (12,10): error CS9109: 'C2' does not implement static interface member 'I1.explicit operator int(C2)'. 'C2.explicit operator int(C2)' cannot implement the interface member because it is not static. + // C2 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotStatic, "I1").WithArguments("C2", "I1." + op + " operator int(C2)", "C2." + op + " operator int(C2)").WithLocation(12, 10), + // (14,30): error CS0558: User-defined operator 'C2.explicit operator int(C2)' must be declared static and public + // public explicit operator int(C2 x) => throw null; + Diagnostic(ErrorCode.ERR_OperatorsMustBeStatic, "int").WithArguments("C2." + op + " operator int(C2)").WithLocation(14, 30), + // (18,10): error CS0737: 'C3' does not implement interface member 'I1.explicit operator int(C3)'. 'C3.explicit operator int(C3)' cannot implement an interface member because it is not public. + // C3 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotPublic, "I1").WithArguments("C3", "I1." + op + " operator int(C3)", "C3." + op + " operator int(C3)").WithLocation(18, 10), + // (20,30): error CS0558: User-defined operator 'C3.explicit operator int(C3)' must be declared static and public + // static explicit operator int(C3 x) => throw null; + Diagnostic(ErrorCode.ERR_OperatorsMustBeStatic, "int").WithArguments("C3." + op + " operator int(C3)").WithLocation(20, 30), + // (24,10): error CS0535: 'C4' does not implement interface member 'I1.explicit operator int(C4)' + // C4 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C4", "I1." + op + " operator int(C4)").WithLocation(24, 10), + // (26,30): error CS9111: Explicit implementation of a user-defined operator 'C4.explicit operator int(C4)' must be declared static + // explicit I1.operator int(C4 x) => throw null; + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C4." + op + " operator int(C4)").WithLocation(26, 30), + // (26,30): error CS0539: 'C4.explicit operator int(C4)' in explicit interface declaration is not found among members of the interface that can be implemented + // explicit I1.operator int(C4 x) => throw null; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "int").WithArguments("C4." + op + " operator int(C4)").WithLocation(26, 30), + // (30,10): error CS0738: 'C5' does not implement interface member 'I1.explicit operator int(C5)'. 'C5.explicit operator long(C5)' cannot implement 'I1.explicit operator int(C5)' because it does not have the matching return type of 'int'. + // C5 : I1 + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberWrongReturnType, "I1").WithArguments("C5", "I1." + op + " operator int(C5)", "C5." + op + " operator long(C5)", "int").WithLocation(30, 10), + // (36,10): error CS0535: 'C6' does not implement interface member 'I1.explicit operator int(C6)' + // C6 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C6", "I1." + op + " operator int(C6)").WithLocation(36, 10), + // (38,37): error CS0539: 'C6.explicit operator long(C6)' in explicit interface declaration is not found among members of the interface that can be implemented + // static explicit I1.operator long(C6 x) => throw null; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "long").WithArguments("C6." + op + " operator long(C6)").WithLocation(38, 37), + // (42,10): error CS0535: 'C7' does not implement interface member 'I1.explicit operator int(C7)' + // C7 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C7", "I1." + op + " operator int(C7)").WithLocation(42, 10), + // (48,10): error CS0535: 'C8' does not implement interface member 'I1.explicit operator int(C8)' + // C8 : I1 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C8", "I1." + op + " operator int(C8)").WithLocation(48, 10), + // (50,23): error CS0539: 'C8.op_Explicit(C8)' in explicit interface declaration is not found among members of the interface that can be implemented + // static int I1.op_Explicit(C8 x) => throw null; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, opName).WithArguments("C8." + opName + "(C8)").WithLocation(50, 23), + // (59,10): error CS0535: 'C9' does not implement interface member 'I2.op_Explicit(C9)' + // C9 : I2 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I2").WithArguments("C9", "I2." + opName + "(C9)").WithLocation(59, 10), + // (65,11): error CS0535: 'C10' does not implement interface member 'I2.op_Explicit(C10)' + // C10 : I2 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I2").WithArguments("C10", "I2." + opName + "(C10)").WithLocation(65, 11), + // (67,38): error CS0539: 'C10.explicit operator int(C10)' in explicit interface declaration is not found among members of the interface that can be implemented + // static explicit I2.operator int(C10 x) => throw null; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "int").WithArguments("C10." + op + " operator int(C10)").WithLocation(67, 38) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticConversionOperator_03([CombinatorialValues("implicit", "explicit")] string op) + { + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator int(T x); +} + +interface I2 : I1 where T : I1 +{} + +interface I3 : I1 where T : I1 +{ + " + op + @" operator int(T x) => default; +} + +interface I4 : I1 where T : I1 +{ + static " + op + @" operator int(T x) => default; +} + +interface I5 : I1 where T : I1 +{ + " + op + @" I1.operator int(T x) => default; +} + +interface I6 : I1 where T : I1 +{ + static " + op + @" I1.operator int(T x) => default; +} + +interface I7 : I1 where T : I1 +{ + abstract static " + op + @" operator int(T x); +} + +public interface I11 where T : I11 +{ + abstract static " + op + @" operator int(T x); +} + +interface I8 : I11 where T : I8 +{ + " + op + @" operator int(T x) => default; +} + +interface I9 : I11 where T : I9 +{ + static " + op + @" operator int(T x) => default; +} + +interface I10 : I11 where T : I10 +{ + abstract static " + op + @" operator int(T x); +} + +interface I12 : I11 where T : I12 +{ + static " + op + @" I11.operator int(T x) => default; +} + +interface I13 : I11 where T : I13 +{ + abstract static " + op + @" I11.operator int(T x); +} + +interface I14 : I1 where T : I1 +{ + abstract static " + op + @" I1.operator int(T x); +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (12,23): error CS0556: User-defined conversion must convert to or from the enclosing type + // implicit operator int(T x) => default; + Diagnostic(ErrorCode.ERR_ConversionNotInvolvingContainedType, "int").WithLocation(12, 23), + // (12,23): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // implicit operator int(T x) => default; + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "int").WithLocation(12, 23), + // (17,30): error CS0556: User-defined conversion must convert to or from the enclosing type + // static implicit operator int(T x) => default; + Diagnostic(ErrorCode.ERR_ConversionNotInvolvingContainedType, "int").WithLocation(17, 30), + // (17,30): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // static implicit operator int(T x) => default; + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "int").WithLocation(17, 30), + // (22,29): error CS9111: Explicit implementation of a user-defined operator 'I5.implicit operator int(T)' must be declared static + // implicit I1.operator int(T x) => default; + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("I5." + op + " operator int(T)").WithLocation(22, 29), + // (22,29): error CS0539: 'I5.implicit operator int(T)' in explicit interface declaration is not found among members of the interface that can be implemented + // implicit I1.operator int(T x) => default; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "int").WithArguments("I5." + op + " operator int(T)").WithLocation(22, 29), + // (27,36): error CS0539: 'I6.implicit operator int(T)' in explicit interface declaration is not found among members of the interface that can be implemented + // static implicit I1.operator int(T x) => default; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "int").WithArguments("I6." + op + " operator int(T)").WithLocation(27, 36), + // (32,39): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + // abstract static implicit operator int(T x); + Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "int").WithLocation(32, 39), + // (42,23): error CS0556: User-defined conversion must convert to or from the enclosing type + // implicit operator int(T x) => default; + Diagnostic(ErrorCode.ERR_ConversionNotInvolvingContainedType, "int").WithLocation(42, 23), + // (42,23): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // implicit operator int(T x) => default; + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "int").WithLocation(42, 23), + // (47,30): error CS0556: User-defined conversion must convert to or from the enclosing type + // static implicit operator int(T x) => default; + Diagnostic(ErrorCode.ERR_ConversionNotInvolvingContainedType, "int").WithLocation(47, 30), + // (47,30): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract + // static implicit operator int(T x) => default; + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "int").WithLocation(47, 30), + // (57,37): error CS0539: 'I12.implicit operator int(T)' in explicit interface declaration is not found among members of the interface that can be implemented + // static implicit I11.operator int(T x) => default; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "int").WithArguments("I12." + op + " operator int(T)").WithLocation(57, 37), + // (62,46): error CS0106: The modifier 'abstract' is not valid for this item + // abstract static implicit I11.operator int(T x); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("abstract").WithLocation(62, 46), + // (62,46): error CS0501: 'I13.implicit operator int(T)' must declare a body because it is not marked abstract, extern, or partial + // abstract static implicit I11.operator int(T x); + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "int").WithArguments("I13." + op + " operator int(T)").WithLocation(62, 46), + // (62,46): error CS0539: 'I13.implicit operator int(T)' in explicit interface declaration is not found among members of the interface that can be implemented + // abstract static implicit I11.operator int(T x); + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "int").WithArguments("I13." + op + " operator int(T)").WithLocation(62, 46), + // (67,45): error CS0106: The modifier 'abstract' is not valid for this item + // abstract static implicit I1.operator int(T x); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("abstract").WithLocation(67, 45), + // (67,45): error CS0501: 'I14.implicit operator int(T)' must declare a body because it is not marked abstract, extern, or partial + // abstract static implicit I1.operator int(T x); + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "int").WithArguments("I14." + op + " operator int(T)").WithLocation(67, 45), + // (67,45): error CS0539: 'I14.implicit operator int(T)' in explicit interface declaration is not found among members of the interface that can be implemented + // abstract static implicit I1.operator int(T x); + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "int").WithArguments("I14." + op + " operator int(T)").WithLocation(67, 45) + ); + + var m01 = compilation1.GlobalNamespace.GetTypeMember("I1").GetMembers().OfType().Single(); + + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I2").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I3").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I4").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I5").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I6").FindImplementationForInterfaceMember(m01)); + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I7").FindImplementationForInterfaceMember(m01)); + + var i8 = compilation1.GlobalNamespace.GetTypeMember("I8"); + Assert.Null(i8.FindImplementationForInterfaceMember(i8.Interfaces().Single().GetMembers().OfType().Single())); + + var i9 = compilation1.GlobalNamespace.GetTypeMember("I9"); + Assert.Null(i9.FindImplementationForInterfaceMember(i9.Interfaces().Single().GetMembers().OfType().Single())); + + var i10 = compilation1.GlobalNamespace.GetTypeMember("I10"); + Assert.Null(i10.FindImplementationForInterfaceMember(i10.Interfaces().Single().GetMembers().OfType().Single())); + + var i12 = compilation1.GlobalNamespace.GetTypeMember("I12"); + Assert.Null(i12.FindImplementationForInterfaceMember(i12.Interfaces().Single().GetMembers().OfType().Single())); + + var i13 = compilation1.GlobalNamespace.GetTypeMember("I13"); + Assert.Null(i13.FindImplementationForInterfaceMember(i13.Interfaces().Single().GetMembers().OfType().Single())); + + Assert.Null(compilation1.GlobalNamespace.GetTypeMember("I14").FindImplementationForInterfaceMember(m01)); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticConversionOperator_04([CombinatorialValues("implicit", "explicit")] string op, bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I2 where T : I2 +{ + abstract static " + op + @" operator int(T x); +} +"; + var source2 = +typeKeyword + @" + Test1 : I2 +{ + static " + op + @" I2.operator int(Test1 x) => default; +} +" + typeKeyword + @" + Test2: I2 +{ + public static " + op + @" operator int(Test2 x) => default; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (4,21): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // static explicit I2.operator int(Test1 x) => default; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "I2.").WithArguments("static abstract members in interfaces").WithLocation(4, 21) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation3.VerifyDiagnostics( + // (4,21): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // static explicit I2.operator int(Test1 x) => default; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "I2.").WithArguments("static abstract members in interfaces").WithLocation(4, 21), + // (14,39): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static explicit operator int(T x); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "int").WithArguments("abstract", "9.0", "preview").WithLocation(14, 39) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticConversionOperator_05([CombinatorialValues("implicit", "explicit")] string op, bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator int(T x); +} +"; + var source2 = +typeKeyword + @" + Test1: I1 +{ + public static " + op + @" operator int(Test1 x) => default; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (2,12): error CS9110: 'Test1.explicit operator int(Test1)' cannot implement interface member 'I1.explicit operator int(Test1)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1." + op + " operator int(Test1)", "I1." + op + " operator int(Test1)", "Test1").WithLocation(2, 12) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation3.VerifyDiagnostics( + // (2,12): error CS9110: 'Test1.explicit operator int(Test1)' cannot implement interface member 'I1.explicit operator int(Test1)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // Test1: I1 + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1." + op + " operator int(Test1)", "I1." + op + " operator int(Test1)", "Test1").WithLocation(2, 12), + // (9,39): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static explicit operator int(T x); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "int").WithLocation(9, 39) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticConversionOperator_06([CombinatorialValues("implicit", "explicit")] string op, bool structure) + { + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator int(T x); +} +"; + var source2 = +typeKeyword + @" + Test1 : I1 +{ + static " + op + @" I1.operator int(Test1 x) => default; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (4,40): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // static explicit I1.operator int(Test1 x) => default; + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "int").WithLocation(4, 40) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation3.VerifyDiagnostics( + // (4,40): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // static explicit I1.operator int(Test1 x) => default; + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "int").WithLocation(4, 40), + // (9,39): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static explicit operator int(T x); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "int").WithLocation(9, 39) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticConversionOperator_07([CombinatorialValues("implicit", "explicit")] string op, bool structure) + { + // Basic implicit implementation scenario, MethodImpl is emitted + + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator int(T x); + abstract static " + op + @" operator long(T x); +} + +" + typeKeyword + @" + C : I1 +{ + public static " + op + @" operator long(C x) => default; + public static " + op + @" operator int(C x) => default; +} +"; + + var opName = ConversionOperatorName(op); + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped, + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false)).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c = module.GlobalNamespace.GetTypeMember("C"); + var i1 = c.Interfaces().Single(); + Assert.Equal(2, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var m01 = i1.GetMembers().OfType().First(); + + var cM01 = (MethodSymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + Assert.False(cM01.IsMetadataVirtual()); + Assert.False(cM01.IsMetadataFinal); + Assert.False(cM01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.Conversion, cM01.MethodKind); + Assert.False(cM01.HasRuntimeSpecialName); + Assert.True(cM01.HasSpecialName); + + Assert.Equal("System.Int32 C." + opName + "(C x)", cM01.ToTestDisplayString()); + + if (module is PEModuleSymbol) + { + Assert.Equal(m01, cM01.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(cM01.ExplicitInterfaceImplementations); + } + + var m02 = i1.GetMembers().OfType().ElementAt(1); + + var cM02 = (MethodSymbol)c.FindImplementationForInterfaceMember(m02); + + Assert.True(cM02.IsStatic); + Assert.False(cM02.IsAbstract); + Assert.False(cM02.IsVirtual); + Assert.False(cM02.IsMetadataVirtual()); + Assert.False(cM02.IsMetadataFinal); + Assert.False(cM02.IsMetadataNewSlot()); + Assert.Equal(MethodKind.Conversion, cM02.MethodKind); + Assert.False(cM02.HasRuntimeSpecialName); + Assert.True(cM02.HasSpecialName); + + Assert.Equal("System.Int64 C." + opName + "(C x)", cM02.ToTestDisplayString()); + + if (module is PEModuleSymbol) + { + Assert.Equal(m02, cM02.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(cM02.ExplicitInterfaceImplementations); + } + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticConversionOperator_08([CombinatorialValues("implicit", "explicit")] string op, bool structure) + { + // Basic explicit implementation scenario + + var typeKeyword = structure ? "struct" : "class"; + + var source1 = +@" +interface I1 where T : I1 +{ + abstract static " + op + @" operator C(T x); + abstract static " + op + @" operator int(T x); +} + +" + typeKeyword + @" + C : I1 +{ + static " + op + @" I1.operator int(C x) => int.MaxValue; + static " + op + @" I1.operator C(C x) => default; +} +"; + + var opName = ConversionOperatorName(op); + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var tree = compilation1.SyntaxTrees.Single(); + var model = compilation1.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + + Assert.Equal("default", node.ToString()); + Assert.Equal("C", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()); + + var declaredSymbol = model.GetDeclaredSymbol(node.FirstAncestorOrSelf()); + Assert.Equal("C C.I1." + opName + "(C x)", declaredSymbol.ToTestDisplayString()); + Assert.DoesNotContain(opName, declaredSymbol.ContainingType.MemberNames); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped, + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false)).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c = module.GlobalNamespace.GetTypeMember("C"); + Assert.Equal(2, c.GetMembers().OfType().Where(m => !m.IsConstructor()).Count()); + + var m01 = c.Interfaces().Single().GetMembers().OfType().First(); + var cM01 = (MethodSymbol)c.FindImplementationForInterfaceMember(m01); + + Assert.True(cM01.IsStatic); + Assert.False(cM01.IsAbstract); + Assert.False(cM01.IsVirtual); + Assert.False(cM01.IsMetadataVirtual()); + Assert.False(cM01.IsMetadataFinal); + Assert.False(cM01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, cM01.MethodKind); + Assert.False(cM01.HasRuntimeSpecialName); + Assert.False(cM01.HasSpecialName); + + Assert.Equal("C C.I1." + opName + "(C x)", cM01.ToTestDisplayString()); + Assert.Equal(m01, cM01.ExplicitInterfaceImplementations.Single()); + + var m02 = c.Interfaces().Single().GetMembers().OfType().ElementAt(1); + var cM02 = (MethodSymbol)c.FindImplementationForInterfaceMember(m02); + + Assert.True(cM02.IsStatic); + Assert.False(cM02.IsAbstract); + Assert.False(cM02.IsVirtual); + Assert.False(cM02.IsMetadataVirtual()); + Assert.False(cM02.IsMetadataFinal); + Assert.False(cM02.IsMetadataNewSlot()); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, cM02.MethodKind); + Assert.False(cM02.HasRuntimeSpecialName); + Assert.False(cM02.HasSpecialName); + + Assert.Equal("System.Int32 C.I1." + opName + "(C x)", cM02.ToTestDisplayString()); + Assert.Equal(m02, cM02.ExplicitInterfaceImplementations.Single()); + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticConversionOperator_09([CombinatorialValues("implicit", "explicit")] string op) + { + // Explicit implementation from base is treated as an implementation + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator int(T x); +} + +public class C2 : I1 +{ + static " + op + @" I1.operator int(C2 x) => default; +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C3 : C2, I1 +{ +} +"; + + var opName = ConversionOperatorName(op); + + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } + + void validate(ModuleSymbol module) + { + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + Assert.Empty(c3.GetMembers().OfType().Where(m => !m.IsConstructor())); + var m01 = c3.Interfaces().Single().GetMembers().OfType().Single(); + + var cM01 = (MethodSymbol)c3.FindImplementationForInterfaceMember(m01); + + Assert.Equal("System.Int32 C2.I1." + opName + "(C2 x)", cM01.ToTestDisplayString()); + Assert.Equal(m01, cM01.ExplicitInterfaceImplementations.Single()); + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticConversionOperator_10([CombinatorialValues("implicit", "explicit")] string op) + { + // Implicit implementation is considered only for types implementing interface in source. + // In metadata, only explicit implementations are considered + + var opName = ConversionOperatorName(op); + + var ilSource = @" +.class interface public auto ansi abstract I1`1<(class I1`1) T> +{ + // Methods + .method public hidebysig specialname abstract virtual static + int32 " + opName + @" ( + !T x + ) cil managed + { + } +} + +.class public auto ansi beforefieldinit C1 + extends System.Object + implements class I1`1 +{ + .method private hidebysig static + int32 'I1." + opName + @"' ( + class C1 x + ) cil managed + { + .override method int32 class I1`1::" + opName + @"(!0) + + IL_0000: ldc.i4.0 + IL_0001: ret + } + + .method public hidebysig static + specialname int32 " + opName + @" ( + class C1 x + ) cil managed + { + IL_0000: ldc.i4.0 + IL_0001: ret + } + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2053 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } +} + +.class public auto ansi beforefieldinit C2 + extends C1 + implements class I1`1 +{ + .method public hidebysig static + specialname int32 " + opName + @" ( + class C1 x + ) cil managed + { + IL_0000: ldc.i4.0 + IL_0001: ret + } + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + IL_0000: ldarg.0 + IL_0001: call instance void C1::.ctor() + IL_0006: ret + } +} +"; + var source1 = +@" +public class C3 : C2 +{ +} + +public class C4 : C1, I1 +{ +} + +public class C5 : C2, I1 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); + + Assert.Equal(MethodKind.Conversion, m01.MethodKind); + Assert.Equal(MethodKind.Conversion, c1.GetMember(opName).MethodKind); + + var c1M01 = (MethodSymbol)c1.FindImplementationForInterfaceMember(m01); + + Assert.Equal("System.Int32 C1.I1." + opName + "(C1 x)", c1M01.ToTestDisplayString()); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c1M01.MethodKind); + Assert.Equal(m01, c1M01.ExplicitInterfaceImplementations.Single()); + + var c2 = compilation1.GlobalNamespace.GetTypeMember("C2"); + Assert.Same(c1M01, c2.FindImplementationForInterfaceMember(m01)); + + var c3 = compilation1.GlobalNamespace.GetTypeMember("C3"); + Assert.Same(c1M01, c3.FindImplementationForInterfaceMember(m01)); + + var c4 = compilation1.GlobalNamespace.GetTypeMember("C4"); + Assert.Same(c1M01, c4.FindImplementationForInterfaceMember(m01)); + + var c5 = compilation1.GlobalNamespace.GetTypeMember("C5"); + + var c2M01 = (MethodSymbol)c5.FindImplementationForInterfaceMember(m01); + Assert.Equal("System.Int32 C2." + opName + "(C1 x)", c2M01.ToTestDisplayString()); + Assert.Equal(MethodKind.Conversion, c2M01.MethodKind); + + compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticConversionOperator_11([CombinatorialValues("implicit", "explicit")] string op) + { + // Ignore invalid metadata (non-abstract static virtual method). + + var opName = ConversionOperatorName(op); + + var ilSource = @" +.class interface public auto ansi abstract I1`1<(class I1`1) T> +{ + // Methods + .method public hidebysig specialname virtual static + int32 " + opName + @" ( + !T x + ) cil managed + { + IL_0000: ldc.i4.0 + IL_0001: ret + } +} +"; + + var source1 = +@" +public class C1 : I1 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyEmitDiagnostics(); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var i1 = c1.Interfaces().Single(); + var m01 = i1.GetMembers().OfType().Single(); + + Assert.Equal(MethodKind.Conversion, m01.MethodKind); + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(i1.FindImplementationForInterfaceMember(m01)); + + compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyEmitDiagnostics(); + + var source2 = +@" +public class C1 : I1 +{ + static " + op + @" I1.operator int(C1 x) => default; +} +"; + + var compilation2 = CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation2.VerifyEmitDiagnostics( + // (4,37): error CS0539: 'C1.implicit operator int(C1)' in explicit interface declaration is not found among members of the interface that can be implemented + // static implicit I1.operator int(C1 x) => default; + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "int").WithArguments("C1." + op + " operator int(C1)").WithLocation(4, 37) + ); + + c1 = compilation2.GlobalNamespace.GetTypeMember("C1"); + m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); + + Assert.Equal("System.Int32 I1." + opName + "(C1 x)", m01.ToTestDisplayString()); + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticConversionOperator_12([CombinatorialValues("implicit", "explicit")] string op) + { + // Ignore invalid metadata (default interface implementation for a static method) + + var opName = ConversionOperatorName(op); + + var ilSource = @" +.class interface public auto ansi abstract I1`1<(class I1`1) T> +{ + // Methods + .method public hidebysig specialname abstract virtual static + int32 " + opName + @" ( + !T x + ) cil managed + { + } +} + +.class interface public auto ansi abstract I2`1<(class I1`1) T> + implements class I1`1 +{ + .method private hidebysig static + int32 'I1." + opName + @"' ( + !T x + ) cil managed + { + .override method int32 class I1`1::" + opName + @"(!0) + + IL_0000: ldc.i4.0 + IL_0001: ret + } +} +"; + + var source1 = +@" +public class C1 : I2 +{ +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyEmitDiagnostics( + // (2,19): error CS0535: 'C1' does not implement interface member 'I1.explicit operator int(C1)' + // public class C1 : I2 + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I2").WithArguments("C1", "I1." + op + " operator int(C1)").WithLocation(2, 19) + ); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + var i2 = c1.Interfaces().Single(); + var i1 = i2.Interfaces().Single(); + var m01 = i1.GetMembers().OfType().Single(); + + Assert.Equal(MethodKind.Conversion, m01.MethodKind); + Assert.Null(c1.FindImplementationForInterfaceMember(m01)); + Assert.Null(i2.FindImplementationForInterfaceMember(m01)); + + var i2M01 = i2.GetMembers().OfType().Single(); + Assert.Equal(m01, i2M01.ExplicitInterfaceImplementations.Single()); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticConversionOperator_13([CombinatorialValues("implicit", "explicit")] string op) + { + // A forwarding method is added for an implicit implementation declared in base class. + + var source1 = +@" +public partial interface I1 where T : I1 +{ + abstract static " + op + @" operator C1(T x); +} + +public partial class C1 +{ + public static " + op + @" operator C1(T x) => default; +} + +public class C2 : C1, I1 +{ +} +"; + + var opName = ConversionOperatorName(op); + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + var i1 = c2.Interfaces().Single(); + var m01 = i1.GetMembers(opName).OfType().Single(); + + var c2M01 = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01); + + Assert.True(c2M01.IsStatic); + Assert.False(c2M01.IsAbstract); + Assert.False(c2M01.IsVirtual); + Assert.False(c2M01.IsMetadataVirtual()); + Assert.False(c2M01.IsMetadataFinal); + Assert.False(c2M01.IsMetadataNewSlot()); + + if (module is PEModuleSymbol) + { + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c2M01.MethodKind); + Assert.False(c2M01.HasRuntimeSpecialName); + Assert.False(c2M01.HasSpecialName); + + Assert.Equal("C1 C2.I1." + opName + "(C2 x)", c2M01.ToTestDisplayString()); + Assert.Equal(m01, c2M01.ExplicitInterfaceImplementations.Single()); + + var c1M01 = module.GlobalNamespace.GetMember("C1." + opName); + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + Assert.False(c1M01.IsMetadataVirtual()); + Assert.False(c1M01.IsMetadataFinal); + Assert.False(c1M01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.Conversion, c1M01.MethodKind); + Assert.False(c1M01.HasRuntimeSpecialName); + Assert.True(c1M01.HasSpecialName); + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + } + else + { + Assert.Equal(MethodKind.Conversion, c2M01.MethodKind); + Assert.False(c2M01.HasRuntimeSpecialName); + Assert.True(c2M01.HasSpecialName); + + Assert.Equal("C1 C1." + opName + "(C2 x)", c2M01.ToTestDisplayString()); + Assert.Empty(c2M01.ExplicitInterfaceImplementations); + } + } + + verifier.VerifyIL("C2.I1." + opName + "(C2)", +@" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""C1 C1." + opName + @"(C2)"" + IL_0006: ret +} +"); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticConversionOperator_14([CombinatorialValues("implicit", "explicit")] string op) + { + // A forwarding method is added for an implicit implementation with modopt mismatch. + + var opName = ConversionOperatorName(op); + + var ilSource = @" +.class interface public auto ansi abstract I1`1<(class I1`1) T> +{ + // Methods + .method public hidebysig specialname abstract virtual static + int32 modopt(I1`1) " + opName + @" ( + !T x + ) cil managed + { + } +} +"; + + var source1 = +@" +class C1 : I1 +{ + public static " + op + @" operator int(C1 x) => default; +} + +class C2 : I1 +{ + static " + op + @" I1.operator int(C2 x) => default; +} +"; + + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var c1 = module.GlobalNamespace.GetTypeMember("C1"); + var m01 = c1.Interfaces().Single().GetMembers().OfType().Single(); + + var c1M01 = (MethodSymbol)c1.FindImplementationForInterfaceMember(m01); + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + Assert.False(c1M01.IsMetadataVirtual()); + Assert.False(c1M01.IsMetadataFinal); + Assert.False(c1M01.IsMetadataNewSlot()); + + if (module is PEModuleSymbol) + { + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c1M01.MethodKind); + Assert.Equal("System.Int32 modopt(I1<>) C1.I1." + opName + "(C1 x)", c1M01.ToTestDisplayString()); + Assert.Equal(m01, c1M01.ExplicitInterfaceImplementations.Single()); + + c1M01 = module.GlobalNamespace.GetMember("C1." + opName); + Assert.Equal("System.Int32 C1." + opName + "(C1 x)", c1M01.ToTestDisplayString()); + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + Assert.False(c1M01.IsMetadataVirtual()); + Assert.False(c1M01.IsMetadataFinal); + Assert.False(c1M01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.Conversion, c1M01.MethodKind); + + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + } + else + { + Assert.Equal(MethodKind.Conversion, c1M01.MethodKind); + Assert.Equal("System.Int32 C1." + opName + "(C1 x)", c1M01.ToTestDisplayString()); + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + } + + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + m01 = c2.Interfaces().Single().GetMembers().OfType().Single(); + var c2M01 = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01); + + Assert.True(c2M01.IsStatic); + Assert.False(c2M01.IsAbstract); + Assert.False(c2M01.IsVirtual); + Assert.False(c2M01.IsMetadataVirtual()); + Assert.False(c2M01.IsMetadataFinal); + Assert.False(c2M01.IsMetadataNewSlot()); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, c2M01.MethodKind); + + Assert.Equal("System.Int32 modopt(I1<>) C2.I1." + opName + "(C2 x)", c2M01.ToTestDisplayString()); + Assert.Equal(m01, c2M01.ExplicitInterfaceImplementations.Single()); + + Assert.Same(c2M01, c2.GetMembers().OfType().Where(m => !m.IsConstructor()).Single()); + } + + verifier.VerifyIL("C1.I1." + opName + "(C1)", +@" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""int C1." + opName + @"(C1)"" + IL_0006: ret +} +"); + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticConversionOperator_15([CombinatorialValues("implicit", "explicit")] string op) + { + // A forwarding method isn't created if base class implements interface exactly the same way. + + var source1 = +@" +public partial interface I1 where T : I1 +{ + abstract static " + op + @" operator C1(T x); + abstract static " + op + @" operator T(int x); +} + +public partial class C1 +{ + public static " + op + @" operator C1(T x) => default; +} + +public class C2 : C1, I1 +{ + static " + op + @" I1.operator C2(int x) => default; +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics(); + + var source2 = +@" +public class C3 : C2, I1 +{ +} +"; + + var opName = ConversionOperatorName(op); + + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.RegularPreview, TestOptions.Regular9 }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } + + void validate(ModuleSymbol module) + { + var c3 = module.GlobalNamespace.GetTypeMember("C3"); + Assert.Empty(c3.GetMembers().OfType().Where(m => !m.IsConstructor())); + + var m01 = c3.Interfaces().Single().GetMembers(opName).OfType().First(); + + var c1M01 = c3.BaseType().BaseType().GetMember(opName); + Assert.Equal("C1 C1." + opName + "(C2 x)", c1M01.ToTestDisplayString()); + + Assert.True(c1M01.IsStatic); + Assert.False(c1M01.IsAbstract); + Assert.False(c1M01.IsVirtual); + Assert.False(c1M01.IsMetadataVirtual()); + Assert.False(c1M01.IsMetadataFinal); + Assert.False(c1M01.IsMetadataNewSlot()); + + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + + if (c1M01.ContainingModule is PEModuleSymbol) + { + var c2M01 = (MethodSymbol)c3.FindImplementationForInterfaceMember(m01); + Assert.Equal("C1 C2.I1." + opName + "(C2 x)", c2M01.ToTestDisplayString()); + Assert.Equal(m01, c2M01.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Equal(c1M01, c3.FindImplementationForInterfaceMember(m01)); + } + + var m02 = c3.Interfaces().Single().GetMembers(opName).OfType().ElementAt(1); + + var c2M02 = c3.BaseType().GetMembers("I1." + opName).OfType().First(); + Assert.Equal("C2 C2.I1." + opName + "(System.Int32 x)", c2M02.ToTestDisplayString()); + Assert.Same(c2M02, c3.FindImplementationForInterfaceMember(m02)); + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticConversionOperator_18([CombinatorialValues("implicit", "explicit")] string op, bool genericFirst) + { + // An "ambiguity" in implicit implementation declared in generic base class plus interface is generic too. + + var generic = +@" + public static " + op + @" operator U(C1 x) => default; +"; + var nonGeneric = +@" + public static " + op + @" operator int(C1 x) => default; +"; + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator U(T x); +} + +public class C1 : I1, U> +{ +" + (genericFirst ? generic + nonGeneric : nonGeneric + generic) + @" +} +"; + + + var opName = ConversionOperatorName(op); + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { CreateCompilation("", targetFramework: TargetFramework.NetCoreApp).ToMetadataReference() }); + + compilation1.VerifyDiagnostics(); + Assert.Equal(2, compilation1.GlobalNamespace.GetTypeMember("C1").GetMembers().Where(m => m.Name.Contains(opName)).Count()); + + var source2 = +@" +public class C2 : C1, I1, int> +{ +} +"; + + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } + + void validate(ModuleSymbol module) + { + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + var m01 = c2.Interfaces().Single().GetMembers(opName).OfType().Single(); + + Assert.True(m01.ContainingModule is RetargetingModuleSymbol or PEModuleSymbol); + + var c1M01 = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01); + Assert.Equal("U C1." + opName + "(C1 x)", c1M01.OriginalDefinition.ToTestDisplayString()); + + var baseI1M01 = c2.BaseType().FindImplementationForInterfaceMember(m01); + Assert.Equal("U C1." + opName + "(C1 x)", baseI1M01.OriginalDefinition.ToTestDisplayString()); + + Assert.Equal(c1M01, baseI1M01); + + if (c1M01.OriginalDefinition.ContainingModule is PEModuleSymbol) + { + Assert.Equal(m01, c1M01.ExplicitInterfaceImplementations.Single()); + } + else + { + Assert.Empty(c1M01.ExplicitInterfaceImplementations); + } + } + } + + [Theory] + [CombinatorialData] + public void ImplementAbstractStaticConversionOperator_20([CombinatorialValues("implicit", "explicit")] string op, bool genericFirst) + { + // Same as ImplementAbstractStaticConversionOperator_18 only implementation is explicit in source. + + var generic = +@" + static " + op + @" I1, U>.operator U(C1 x) => default; +"; + var nonGeneric = +@" + public static " + op + @" operator int(C1 x) => default; +"; + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator U(T x); +} + +public class C1 : I1, U> +{ +" + (genericFirst ? generic + nonGeneric : nonGeneric + generic) + @" +} +"; + var opName = ConversionOperatorName(op); + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { CreateCompilation("", targetFramework: TargetFramework.NetCoreApp).ToMetadataReference() }); + + compilation1.VerifyDiagnostics(); + Assert.Equal(2, compilation1.GlobalNamespace.GetTypeMember("C1").GetMembers().Where(m => m.Name.Contains(opName)).Count()); + + var source2 = +@" +public class C2 : C1, I1, int> +{ +} +"; + + foreach (var reference in new[] { compilation1.ToMetadataReference(), compilation1.EmitToImageReference() }) + { + foreach (var parseOptions in new[] { TestOptions.Regular9, TestOptions.RegularPreview }) + { + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: parseOptions, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { reference }); + + CompileAndVerify(compilation2, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + } + } + + void validate(ModuleSymbol module) + { + var c2 = module.GlobalNamespace.GetTypeMember("C2"); + var m01 = c2.Interfaces().Single().GetMembers(opName).OfType().Single(); + + Assert.True(m01.ContainingModule is RetargetingModuleSymbol or PEModuleSymbol); + + var c1M01 = (MethodSymbol)c2.FindImplementationForInterfaceMember(m01); + Assert.Equal("U C1.I1, U>." + opName + "(C1 x)", c1M01.OriginalDefinition.ToTestDisplayString()); + + Assert.Equal(m01, c1M01.ExplicitInterfaceImplementations.Single()); + Assert.Same(c1M01, c2.BaseType().FindImplementationForInterfaceMember(m01)); + } + } + + [Theory] + [CombinatorialData] + public void ExplicitImplementationModifiersConversionOperator_01([CombinatorialValues("implicit", "explicit")] string op) + { + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator int(T x); +} + +class + C1 : I1 +{ + static " + op + @" I1.operator int(C1 x) => default; +} + +class + C2 : I1 +{ + private static " + op + @" I1.operator int(C2 x) => default; +} + +class + C3 : I1 +{ + protected static " + op + @" I1.operator int(C3 x) => default; +} + +class + C4 : I1 +{ + internal static " + op + @" I1.operator int(C4 x) => default; +} + +class + C5 : I1 +{ + protected internal static " + op + @" I1.operator int(C5 x) => default; +} + +class + C6 : I1 +{ + private protected static " + op + @" I1.operator int(C6 x) => default; +} + +class + C7 : I1 +{ + public static " + op + @" I1.operator int(C7 x) => default; +} + +class + C9 : I1 +{ + async static " + op + @" I1.operator int(C9 x) => default; +} + +class + C10 : I1 +{ + unsafe static " + op + @" I1.operator int(C10 x) => default; +} + +class + C11 : I1 +{ + static readonly " + op + @" I1.operator int(C11 x) => default; +} + +class + C12 : I1 +{ + extern static " + op + @" I1.operator int(C12 x); +} + +class + C13 : I1 +{ + abstract static " + op + @" I1.operator int(C13 x) => default; +} + +class + C14 : I1 +{ + virtual static " + op + @" I1.operator int(C14 x) => default; +} + +class + C15 : I1 +{ + sealed static " + op + @" I1.operator int(C15 x) => default; +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll.WithAllowUnsafe(true), + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var c1 = compilation1.GlobalNamespace.GetTypeMember("C1"); + + Assert.Equal(Accessibility.Private, c1.GetMembers().OfType().Where(m => !m.IsConstructor()).Single().DeclaredAccessibility); + + compilation1.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.WRN_ExternMethodNoImplementation).Verify( + // (16,45): error CS0106: The modifier 'private' is not valid for this item + // private static explicit I1.operator int(C2 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("private").WithLocation(16, 45), + // (22,47): error CS0106: The modifier 'protected' is not valid for this item + // protected static explicit I1.operator int(C3 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("protected").WithLocation(22, 47), + // (28,46): error CS0106: The modifier 'internal' is not valid for this item + // internal static explicit I1.operator int(C4 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("internal").WithLocation(28, 46), + // (34,56): error CS0106: The modifier 'protected internal' is not valid for this item + // protected internal static explicit I1.operator int(C5 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("protected internal").WithLocation(34, 56), + // (40,55): error CS0106: The modifier 'private protected' is not valid for this item + // private protected static explicit I1.operator int(C6 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("private protected").WithLocation(40, 55), + // (46,44): error CS0106: The modifier 'public' is not valid for this item + // public static explicit I1.operator int(C7 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("public").WithLocation(46, 44), + // (52,43): error CS0106: The modifier 'async' is not valid for this item + // async static explicit I1.operator int(C9 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("async").WithLocation(52, 43), + // (64,47): error CS0106: The modifier 'readonly' is not valid for this item + // static readonly explicit I1.operator int(C11 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("readonly").WithLocation(64, 47), + // (76,47): error CS0106: The modifier 'abstract' is not valid for this item + // abstract static explicit I1.operator int(C13 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("abstract").WithLocation(76, 47), + // (82,46): error CS0106: The modifier 'virtual' is not valid for this item + // virtual static explicit I1.operator int(C14 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("virtual").WithLocation(82, 46), + // (88,45): error CS0106: The modifier 'sealed' is not valid for this item + // sealed static explicit I1.operator int(C15 x) => default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("sealed").WithLocation(88, 45) + ); + } + + [Theory] + [CombinatorialData] + public void ExplicitInterfaceSpecifierErrorsConversionOperator_01([CombinatorialValues("implicit", "explicit")] string op) + { + var source1 = +@" +public interface I1 where T : struct, I1 +{ + abstract static " + op + @" operator int(T x); +} + +class C1 +{ + static " + op + @" I1.operator int(int x) => default; +} + +class C2 : I1 +{ + static " + op + @" I1.operator int(C2 x) => default; +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (9,21): error CS0540: 'C1.I1.implicit operator int(int)': containing type does not implement interface 'I1' + // static implicit I1.operator int(int x) => default; + Diagnostic(ErrorCode.ERR_ClassDoesntImplementInterface, "I1").WithArguments("C1.I1." + op + " operator int(int)", "I1").WithLocation(9, 21), + // (9,21): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'I1'. There is no boxing conversion from 'int' to 'I1'. + // static implicit I1.operator int(int x) => default; + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "I1").WithArguments("I1", "I1", "T", "int").WithLocation(9, 21), + // (12,7): error CS0453: The type 'C2' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'I1' + // class C2 : I1 + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "C2").WithArguments("I1", "T", "C2").WithLocation(12, 7), + // (14,21): error CS0453: The type 'C2' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'I1' + // static implicit I1.operator int(C2 x) => default; + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "I1").WithArguments("I1", "T", "C2").WithLocation(14, 21) + ); + } } } From 07c3c6aaa3af7c58cfef026b7387949a994d5485 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Fri, 21 May 2021 13:54:33 -0700 Subject: [PATCH 087/127] multiple blank lines are the bane of my existance --- .../Test/ValueTracking/AbstractBaseValueTrackingTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs index aa89642307f9d..549ff737d21ef 100644 --- a/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs +++ b/src/EditorFeatures/Test/ValueTracking/AbstractBaseValueTrackingTests.cs @@ -24,7 +24,6 @@ protected TestWorkspace CreateWorkspace(string code, TestHost testHost) protected abstract TestWorkspace CreateWorkspace(string code, TestComposition composition); - internal static async Task> GetTrackedItemsAsync(TestWorkspace testWorkspace, CancellationToken cancellationToken = default) { var cursorDocument = testWorkspace.DocumentWithCursor; From 4264c04574755b186e68888e7540e1ae36db8d62 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Thu, 27 May 2021 09:27:39 -0700 Subject: [PATCH 088/127] Enable consumption of user-defined conversion operators declared in interfaces. (#53619) Related to https://github.com/dotnet/csharplang/pull/4773. --- .../Portable/Binder/Binder_Conversions.cs | 16 + .../Portable/Binder/ForEachLoopBinder.cs | 2 + .../Semantics/Conversions/Conversion.cs | 19 + .../UserDefinedConversionAnalysis.cs | 7 + .../Conversions/UserDefinedConversions.cs | 86 +- .../UserDefinedExplicitConversions.cs | 58 +- .../UserDefinedImplicitConversions.cs | 56 +- .../DiagnosticsPass_ExpressionTrees.cs | 5 + .../LocalRewriter/LocalRewriter_Conversion.cs | 15 +- .../Lowering/MethodToClassRewriter.cs | 1 + .../StaticAbstractMembersInInterfacesTests.cs | 1103 +++++++++++++++++ 11 files changed, 1308 insertions(+), 60 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 56c9eebe7e9e2..f1de381c149ec 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -346,6 +346,7 @@ private BoundExpression CreateUserDefinedConversion( bool hasErrors) { Debug.Assert(conversionGroup != null); + Debug.Assert(conversion.IsUserDefined); if (!conversion.IsValid) { @@ -365,6 +366,21 @@ private BoundExpression CreateUserDefinedConversion( { WasCompilerGenerated = source.WasCompilerGenerated }; } + if (conversion.Method is MethodSymbol method && method.IsStatic && method.IsAbstract) + { + Debug.Assert(conversion.ConstrainedToTypeOpt is TypeParameterSymbol); + + if (Compilation.SourceModule != method.ContainingModule) + { + CheckFeatureAvailability(syntax, MessageID.IDS_FeatureStaticAbstractMembersInInterfaces, diagnostics); + + if (!Compilation.Assembly.RuntimeSupportsStaticAbstractMembersInInterfaces) + { + Error(diagnostics, ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, syntax); + } + } + } + // Due to an oddity in the way we create a non-lifted user-defined conversion from A to D? // (required backwards compatibility with the native compiler) we can end up in a situation // where we have: diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index b5dad2ee17e58..aac4b1ea9ebde 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -493,6 +493,7 @@ private BoundForEachStatement BindForEachPartsWorker(BindingDiagnosticBag diagno // We're wrapping the collection expression in a (non-synthesized) conversion so that its converted // type (i.e. builder.CollectionType) will be available in the binding API. + Debug.Assert(!builder.CollectionConversion.IsUserDefined); BoundConversion convertedCollectionExpression = new BoundConversion( collectionExpr.Syntax, collectionExpr, @@ -1259,6 +1260,7 @@ private MethodArgumentInfo FindForEachPatternMethodViaExtension(BoundExpression diagnostics.Add(_syntax, useSiteInfo); // Unconditionally convert here, to match what we set the ConvertedExpression to in the main BoundForEachStatement node. + Debug.Assert(!collectionConversion.IsUserDefined); collectionExpr = new BoundConversion( collectionExpr.Syntax, collectionExpr, diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs index b3a84335348d5..afcffedc0426b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs @@ -382,6 +382,25 @@ internal MethodSymbol? Method } } + internal TypeParameterSymbol? ConstrainedToTypeOpt + { + get + { + var uncommonData = _uncommonData; + if (uncommonData != null && uncommonData._conversionMethod is null) + { + var conversionResult = uncommonData._conversionResult; + if (conversionResult.Kind == UserDefinedConversionResultKind.Valid) + { + UserDefinedConversionAnalysis analysis = conversionResult.Results[conversionResult.Best]; + return analysis.ConstrainedToTypeOpt; + } + } + + return null; + } + } + internal DeconstructMethodInfo DeconstructionInfo { get diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedConversionAnalysis.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedConversionAnalysis.cs index f6d2846eaff3b..56a7e2e00f34c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedConversionAnalysis.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedConversionAnalysis.cs @@ -20,6 +20,7 @@ internal sealed class UserDefinedConversionAnalysis { public readonly TypeSymbol FromType; public readonly TypeSymbol ToType; + public readonly TypeParameterSymbol ConstrainedToTypeOpt; public readonly MethodSymbol Operator; public readonly Conversion SourceConversion; @@ -27,6 +28,7 @@ internal sealed class UserDefinedConversionAnalysis public readonly UserDefinedConversionAnalysisKind Kind; public static UserDefinedConversionAnalysis Normal( + TypeParameterSymbol constrainedToTypeOpt, MethodSymbol op, Conversion sourceConversion, Conversion targetConversion, @@ -35,6 +37,7 @@ public static UserDefinedConversionAnalysis Normal( { return new UserDefinedConversionAnalysis( UserDefinedConversionAnalysisKind.ApplicableInNormalForm, + constrainedToTypeOpt, op, sourceConversion, targetConversion, @@ -43,6 +46,7 @@ public static UserDefinedConversionAnalysis Normal( } public static UserDefinedConversionAnalysis Lifted( + TypeParameterSymbol constrainedToTypeOpt, MethodSymbol op, Conversion sourceConversion, Conversion targetConversion, @@ -51,6 +55,7 @@ public static UserDefinedConversionAnalysis Lifted( { return new UserDefinedConversionAnalysis( UserDefinedConversionAnalysisKind.ApplicableInLiftedForm, + constrainedToTypeOpt, op, sourceConversion, targetConversion, @@ -60,6 +65,7 @@ public static UserDefinedConversionAnalysis Lifted( private UserDefinedConversionAnalysis( UserDefinedConversionAnalysisKind kind, + TypeParameterSymbol constrainedToTypeOpt, MethodSymbol op, Conversion sourceConversion, Conversion targetConversion, @@ -67,6 +73,7 @@ private UserDefinedConversionAnalysis( TypeSymbol toType) { this.Kind = kind; + this.ConstrainedToTypeOpt = constrainedToTypeOpt; this.Operator = op; this.SourceConversion = sourceConversion; this.TargetConversion = targetConversion; diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedConversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedConversions.cs index b594d2e69625e..0ab8f38f580d3 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedConversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedConversions.cs @@ -15,8 +15,11 @@ namespace Microsoft.CodeAnalysis.CSharp { internal partial class ConversionsBase { - private static TypeSymbol GetUnderlyingEffectiveType(TypeSymbol type, ref CompoundUseSiteInfo useSiteInfo) + public static void AddTypesParticipatingInUserDefinedConversion(ArrayBuilder result, TypeSymbol type, bool includeBaseTypes, ref CompoundUseSiteInfo useSiteInfo) { + // CONSIDER: These sets are usually small; if they are large then this is an O(n^2) + // CONSIDER: algorithm. We could use a hash table instead to build up the set. + // Spec 6.4.4: User-defined implicit conversions // Spec 6.4.5: User-defined explicit conversions // @@ -24,57 +27,80 @@ private static TypeSymbol GetUnderlyingEffectiveType(TypeSymbol type, ref Compou // * If S or T are nullable types, let Su and Tu be their underlying types, otherwise let Su and Tu be S and T, respectively. // * If Su or Tu are type parameters, S0 and T0 are their effective base types, otherwise S0 and T0 are equal to Su and Tu, respectively. - if ((object)type != null) - { - type = type.StrippedType(); - - if (type.IsTypeParameter()) - { - type = ((TypeParameterSymbol)type).EffectiveBaseClass(ref useSiteInfo); - } - } + // Spec 6.4.4: User-defined implicit conversions + // Find the set of types D from which user-defined conversion operators + // will be considered. This set consists of S0 (if S0 is a class or struct), + // the base classes of S0 (if S0 is a class), and T0 (if T0 is a class or struct). + // + // Spec 6.4.5: User-defined explicit conversions + // Find the set of types, D, from which user-defined conversion operators will be considered. + // This set consists of S0 (if S0 is a class or struct), the base classes of S0 (if S0 is a class), + // T0 (if T0 is a class or struct), and the base classes of T0 (if T0 is a class). - return type; - } + // PROTOTYPE(StaticAbstractMembersInInterfaces): Adjust the above specification quote appropriately. - public static void AddTypesParticipatingInUserDefinedConversion(ArrayBuilder result, TypeSymbol type, bool includeBaseTypes, ref CompoundUseSiteInfo useSiteInfo) - { if ((object)type == null) { return; } - // CONSIDER: These sets are usually small; if they are large then this is an O(n^2) - // CONSIDER: algorithm. We could use a hash table instead to build up the set. - - Debug.Assert(!type.IsTypeParameter()); + type = type.StrippedType(); // optimization: bool excludeExisting = result.Count > 0; - if (type.IsClassType() || type.IsStructType()) + if (type is TypeParameterSymbol typeParameter) { - var namedType = (NamedTypeSymbol)type; - if (!excludeExisting || !HasIdentityConversionToAny(namedType, result)) + NamedTypeSymbol effectiveBaseClass = typeParameter.EffectiveBaseClass(ref useSiteInfo); + + addFromClassOrStruct(result, excludeExisting, effectiveBaseClass, includeBaseTypes, ref useSiteInfo); + + switch (effectiveBaseClass.SpecialType) { - result.Add(namedType); + case SpecialType.System_ValueType: + case SpecialType.System_Enum: + case SpecialType.System_Array: + case SpecialType.System_Object: + if (!excludeExisting || !HasIdentityConversionToAny(typeParameter, result)) + { + // Add the type parameter to the set as well. This will be treated equivalent to adding its + // effective interfaces to the set. We are not doing that here because we still need to know + // the originating type parameter as "constrained to" type. + result.Add(typeParameter); + } + break; } } - - if (!includeBaseTypes) + else { - return; + addFromClassOrStruct(result, excludeExisting, type, includeBaseTypes, ref useSiteInfo); } - NamedTypeSymbol t = type.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteInfo); - while ((object)t != null) + static void addFromClassOrStruct(ArrayBuilder result, bool excludeExisting, TypeSymbol type, bool includeBaseTypes, ref CompoundUseSiteInfo useSiteInfo) { - if (!excludeExisting || !HasIdentityConversionToAny(t, result)) + if (type.IsClassType() || type.IsStructType()) { - result.Add(t); + if (!excludeExisting || !HasIdentityConversionToAny(type, result)) + { + result.Add(type); + } } - t = t.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteInfo); + if (!includeBaseTypes) + { + return; + } + + NamedTypeSymbol t = type.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteInfo); + while ((object)t != null) + { + if (!excludeExisting || !HasIdentityConversionToAny(t, result)) + { + result.Add(t); + } + + t = t.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteInfo); + } } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedExplicitConversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedExplicitConversions.cs index ae5d2e54b7ec5..63aa33c7cd866 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedExplicitConversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedExplicitConversions.cs @@ -31,7 +31,7 @@ private UserDefinedConversionResult AnalyzeExplicitUserDefinedConversions( // SPEC: Find the set of types D from which user-defined conversion operators // SPEC: will be considered... - var d = ArrayBuilder.GetInstance(); + var d = ArrayBuilder.GetInstance(); ComputeUserDefinedExplicitConversionTypeSet(source, target, d, ref useSiteInfo); // SPEC: Find the set of applicable user-defined and lifted conversion operators, U... @@ -69,25 +69,22 @@ private UserDefinedConversionResult AnalyzeExplicitUserDefinedConversions( return UserDefinedConversionResult.Valid(u, best.Value); } - private static void ComputeUserDefinedExplicitConversionTypeSet(TypeSymbol source, TypeSymbol target, ArrayBuilder d, ref CompoundUseSiteInfo useSiteInfo) + private static void ComputeUserDefinedExplicitConversionTypeSet(TypeSymbol source, TypeSymbol target, ArrayBuilder d, ref CompoundUseSiteInfo useSiteInfo) { - TypeSymbol s0 = GetUnderlyingEffectiveType(source, ref useSiteInfo); - TypeSymbol t0 = GetUnderlyingEffectiveType(target, ref useSiteInfo); - // Spec 6.4.5: User-defined explicit conversions // Find the set of types, D, from which user-defined conversion operators will be considered. // This set consists of S0 (if S0 is a class or struct), the base classes of S0 (if S0 is a class), // T0 (if T0 is a class or struct), and the base classes of T0 (if T0 is a class). - AddTypesParticipatingInUserDefinedConversion(d, s0, includeBaseTypes: true, useSiteInfo: ref useSiteInfo); - AddTypesParticipatingInUserDefinedConversion(d, t0, includeBaseTypes: true, useSiteInfo: ref useSiteInfo); + AddTypesParticipatingInUserDefinedConversion(d, source, includeBaseTypes: true, useSiteInfo: ref useSiteInfo); + AddTypesParticipatingInUserDefinedConversion(d, target, includeBaseTypes: true, useSiteInfo: ref useSiteInfo); } private void ComputeApplicableUserDefinedExplicitConversionSet( BoundExpression sourceExpression, TypeSymbol source, TypeSymbol target, - ArrayBuilder d, + ArrayBuilder d, ArrayBuilder u, ref CompoundUseSiteInfo useSiteInfo) { @@ -96,10 +93,44 @@ private void ComputeApplicableUserDefinedExplicitConversionSet( Debug.Assert(d != null); Debug.Assert(u != null); - foreach (NamedTypeSymbol declaringType in d) + HashSet lookedInInterfaces = null; + + foreach (TypeSymbol declaringType in d) + { + if (declaringType is TypeParameterSymbol typeParameter) + { + ImmutableArray interfaceTypes = typeParameter.AllEffectiveInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo); + + if (!interfaceTypes.IsEmpty) + { + lookedInInterfaces ??= new HashSet(Symbols.SymbolEqualityComparer.AllIgnoreOptions); // Equivalent to has identity conversion check + + foreach (var interfaceType in interfaceTypes) + { + if (lookedInInterfaces.Add(interfaceType)) + { + addCandidatesFromType(constrainedToTypeOpt: typeParameter, interfaceType, sourceExpression, source, target, u, ref useSiteInfo); + } + } + } + } + else + { + addCandidatesFromType(constrainedToTypeOpt: null, (NamedTypeSymbol)declaringType, sourceExpression, source, target, u, ref useSiteInfo); + } + } + + void addCandidatesFromType( + TypeParameterSymbol constrainedToTypeOpt, + NamedTypeSymbol declaringType, + BoundExpression sourceExpression, + TypeSymbol source, + TypeSymbol target, + ArrayBuilder u, + ref CompoundUseSiteInfo useSiteInfo) { - AddUserDefinedConversionsToExplicitCandidateSet(sourceExpression, source, target, u, declaringType, WellKnownMemberNames.ExplicitConversionName, ref useSiteInfo); - AddUserDefinedConversionsToExplicitCandidateSet(sourceExpression, source, target, u, declaringType, WellKnownMemberNames.ImplicitConversionName, ref useSiteInfo); + AddUserDefinedConversionsToExplicitCandidateSet(sourceExpression, source, target, u, constrainedToTypeOpt, declaringType, WellKnownMemberNames.ExplicitConversionName, ref useSiteInfo); + AddUserDefinedConversionsToExplicitCandidateSet(sourceExpression, source, target, u, constrainedToTypeOpt, declaringType, WellKnownMemberNames.ImplicitConversionName, ref useSiteInfo); } } @@ -108,6 +139,7 @@ private void AddUserDefinedConversionsToExplicitCandidateSet( TypeSymbol source, TypeSymbol target, ArrayBuilder u, + TypeParameterSymbol constrainedToTypeOpt, NamedTypeSymbol declaringType, string operatorName, ref CompoundUseSiteInfo useSiteInfo) @@ -241,7 +273,7 @@ private void AddUserDefinedConversionsToExplicitCandidateSet( Conversion liftedToConversion = EncompassingExplicitConversion(null, nullableTo, target, ref useSiteInfo); Debug.Assert(liftedFromConversion.Exists); Debug.Assert(liftedToConversion.Exists); - u.Add(UserDefinedConversionAnalysis.Lifted(op, liftedFromConversion, liftedToConversion, nullableFrom, nullableTo)); + u.Add(UserDefinedConversionAnalysis.Lifted(constrainedToTypeOpt, op, liftedFromConversion, liftedToConversion, nullableFrom, nullableTo)); } else { @@ -272,7 +304,7 @@ private void AddUserDefinedConversionsToExplicitCandidateSet( fromConversion = EncompassingExplicitConversion(null, convertsFrom, source, ref useSiteInfo); } - u.Add(UserDefinedConversionAnalysis.Normal(op, fromConversion, toConversion, convertsFrom, convertsTo)); + u.Add(UserDefinedConversionAnalysis.Normal(constrainedToTypeOpt, op, fromConversion, toConversion, convertsFrom, convertsTo)); } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs index 63ce6a34148aa..4bc7e128466c8 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs @@ -76,7 +76,7 @@ private UserDefinedConversionResult AnalyzeImplicitUserDefinedConversions( // A user-defined implicit conversion from an expression E to type T is processed as follows: // SPEC: Find the set of types D from which user-defined conversion operators... - var d = ArrayBuilder.GetInstance(); + var d = ArrayBuilder.GetInstance(); ComputeUserDefinedImplicitConversionTypeSet(source, target, d, ref useSiteInfo); // SPEC: Find the set of applicable user-defined and lifted conversion operators, U... @@ -114,18 +114,15 @@ private UserDefinedConversionResult AnalyzeImplicitUserDefinedConversions( return UserDefinedConversionResult.Valid(u, best.Value); } - private static void ComputeUserDefinedImplicitConversionTypeSet(TypeSymbol s, TypeSymbol t, ArrayBuilder d, ref CompoundUseSiteInfo useSiteInfo) + private static void ComputeUserDefinedImplicitConversionTypeSet(TypeSymbol s, TypeSymbol t, ArrayBuilder d, ref CompoundUseSiteInfo useSiteInfo) { // Spec 6.4.4: User-defined implicit conversions // Find the set of types D from which user-defined conversion operators // will be considered. This set consists of S0 (if S0 is a class or struct), // the base classes of S0 (if S0 is a class), and T0 (if T0 is a class or struct). - TypeSymbol s0 = GetUnderlyingEffectiveType(s, ref useSiteInfo); - TypeSymbol t0 = GetUnderlyingEffectiveType(t, ref useSiteInfo); - - AddTypesParticipatingInUserDefinedConversion(d, s0, includeBaseTypes: true, useSiteInfo: ref useSiteInfo); - AddTypesParticipatingInUserDefinedConversion(d, t0, includeBaseTypes: false, useSiteInfo: ref useSiteInfo); + AddTypesParticipatingInUserDefinedConversion(d, s, includeBaseTypes: true, useSiteInfo: ref useSiteInfo); + AddTypesParticipatingInUserDefinedConversion(d, t, includeBaseTypes: false, useSiteInfo: ref useSiteInfo); } /// @@ -145,7 +142,7 @@ private void ComputeApplicableUserDefinedImplicitConversionSet( BoundExpression sourceExpression, TypeSymbol source, TypeSymbol target, - ArrayBuilder d, + ArrayBuilder d, ArrayBuilder u, ref CompoundUseSiteInfo useSiteInfo, bool allowAnyTarget = false) @@ -246,7 +243,42 @@ private void ComputeApplicableUserDefinedImplicitConversionSet( return; } - foreach (NamedTypeSymbol declaringType in d) + HashSet lookedInInterfaces = null; + + foreach (TypeSymbol declaringType in d) + { + if (declaringType is TypeParameterSymbol typeParameter) + { + ImmutableArray interfaceTypes = typeParameter.AllEffectiveInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo); + + if (!interfaceTypes.IsEmpty) + { + lookedInInterfaces ??= new HashSet(Symbols.SymbolEqualityComparer.AllIgnoreOptions); // Equivalent to has identity conversion check + + foreach (var interfaceType in interfaceTypes) + { + if (lookedInInterfaces.Add(interfaceType)) + { + addCandidatesFromType(constrainedToTypeOpt: typeParameter, interfaceType, sourceExpression, source, target, u, ref useSiteInfo, allowAnyTarget); + } + } + } + } + else + { + addCandidatesFromType(constrainedToTypeOpt: null, (NamedTypeSymbol)declaringType, sourceExpression, source, target, u, ref useSiteInfo, allowAnyTarget); + } + } + + void addCandidatesFromType( + TypeParameterSymbol constrainedToTypeOpt, + NamedTypeSymbol declaringType, + BoundExpression sourceExpression, + TypeSymbol source, + TypeSymbol target, + ArrayBuilder u, + ref CompoundUseSiteInfo useSiteInfo, + bool allowAnyTarget) { foreach (MethodSymbol op in declaringType.GetOperators(WellKnownMemberNames.ImplicitConversionName)) { @@ -281,7 +313,7 @@ private void ComputeApplicableUserDefinedImplicitConversionSet( EncompassingImplicitConversion(null, convertsTo, target, ref useSiteInfo); } - u.Add(UserDefinedConversionAnalysis.Normal(op, fromConversion, toConversion, convertsFrom, convertsTo)); + u.Add(UserDefinedConversionAnalysis.Normal(constrainedToTypeOpt, op, fromConversion, toConversion, convertsFrom, convertsTo)); } else if ((object)source != null && source.IsNullableType() && convertsFrom.IsNonNullableValueType() && (allowAnyTarget || target.CanBeAssignedNull())) @@ -309,7 +341,7 @@ private void ComputeApplicableUserDefinedImplicitConversionSet( Conversion.Identity; if (liftedFromConversion.Exists && liftedToConversion.Exists) { - u.Add(UserDefinedConversionAnalysis.Lifted(op, liftedFromConversion, liftedToConversion, nullableFrom, nullableTo)); + u.Add(UserDefinedConversionAnalysis.Lifted(constrainedToTypeOpt, op, liftedFromConversion, liftedToConversion, nullableFrom, nullableTo)); } } } @@ -889,7 +921,7 @@ protected UserDefinedConversionResult AnalyzeImplicitUserDefinedConversionForV6S // SPEC VIOLATION: We do the same to maintain compatibility with the native compiler. // (a) Compute the set of types D from which user-defined conversion operators should be considered by considering only the source type. - var d = ArrayBuilder.GetInstance(); + var d = ArrayBuilder.GetInstance(); ComputeUserDefinedImplicitConversionTypeSet(source, t: null, d: d, useSiteInfo: ref useSiteInfo); // (b) Instead of computing applicable user defined implicit conversions U from the source type to a specific target type, diff --git a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs index 1cd7b452dfa1c..d018884a9ba9d 100644 --- a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs +++ b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs @@ -739,6 +739,11 @@ public override BoundNode VisitConversion(BoundConversion node) break; default: + + if (_inExpressionLambda && node.Conversion.Method is MethodSymbol method && method.IsAbstract && method.IsStatic) + { + Error(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, node); + } break; } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index f0677f4442d72..31d93688df3da 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -936,7 +936,8 @@ private BoundExpression RewriteFullyLiftedBuiltInConversion( if (nonNullValue != null) { Debug.Assert(conversion.Method is { }); - return MakeLiftedUserDefinedConversionConsequence(BoundCall.Synthesized(syntax, receiverOpt: null, conversion.Method, nonNullValue), type); + var constrainedToTypeOpt = conversion.ConstrainedToTypeOpt; + return MakeLiftedUserDefinedConversionConsequence(BoundCall.Synthesized(syntax, receiverOpt: constrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, constrainedToTypeOpt), conversion.Method, nonNullValue), type); } return DistributeLiftedConversionIntoLiftedOperand(syntax, operand, conversion, false, type); @@ -1073,7 +1074,8 @@ private BoundExpression RewriteUserDefinedConversion( return new BoundReadOnlySpanFromArray(syntax, rewrittenOperand, conversion.Method, rewrittenType) { WasCompilerGenerated = true }; } - BoundExpression result = BoundCall.Synthesized(syntax, receiverOpt: null, conversion.Method, rewrittenOperand); + var constrainedToTypeOpt = conversion.ConstrainedToTypeOpt; + BoundExpression result = BoundCall.Synthesized(syntax, receiverOpt: constrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, constrainedToTypeOpt), conversion.Method, rewrittenOperand); Debug.Assert(TypeSymbol.Equals(result.Type, rewrittenType, TypeCompareKind.ConsiderEverything2)); return result; } @@ -1143,7 +1145,8 @@ private BoundExpression RewriteLiftedUserDefinedConversion( // op_Whatever(temp.GetValueOrDefault()) Debug.Assert(conversion.Method is { }); - BoundCall userDefinedCall = BoundCall.Synthesized(syntax, receiverOpt: null, conversion.Method, callGetValueOrDefault); + var constrainedToTypeOpt = conversion.ConstrainedToTypeOpt; + BoundCall userDefinedCall = BoundCall.Synthesized(syntax, receiverOpt: constrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, constrainedToTypeOpt), conversion.Method, callGetValueOrDefault); // new R?(op_Whatever(temp.GetValueOrDefault()) BoundExpression consequence = MakeLiftedUserDefinedConversionConsequence(userDefinedCall, rewrittenType); @@ -1474,7 +1477,7 @@ private Conversion TryMakeConversion(SyntaxNode syntax, Conversion conversion, T else { // TODO: how do we distinguish from normal and lifted conversions here? - var analysis = UserDefinedConversionAnalysis.Normal(meth, fromConversion, toConversion, fromType, toType); + var analysis = UserDefinedConversionAnalysis.Normal(conversion.ConstrainedToTypeOpt, meth, fromConversion, toConversion, fromType, toType); var result = UserDefinedConversionResult.Valid(ImmutableArray.Create(analysis), 0); return new Conversion(result, conversion.IsImplicit); } @@ -1556,6 +1559,8 @@ private Conversion TryMakeConversion(SyntaxNode syntax, TypeSymbol fromType, Typ /// private Conversion TryMakeUserDefinedConversion(SyntaxNode syntax, MethodSymbol meth, TypeSymbol fromType, TypeSymbol toType, bool isImplicit = true) { + Debug.Assert(!meth.ContainingType.IsInterface); + Conversion fromConversion = TryMakeConversion(syntax, fromType, meth.Parameters[0].Type); if (!fromConversion.Exists) { @@ -1569,7 +1574,7 @@ private Conversion TryMakeUserDefinedConversion(SyntaxNode syntax, MethodSymbol } // TODO: distinguish between normal and lifted conversions here - var analysis = UserDefinedConversionAnalysis.Normal(meth, fromConversion, toConversion, fromType, toType); + var analysis = UserDefinedConversionAnalysis.Normal(constrainedToTypeOpt: null, meth, fromConversion, toConversion, fromType, toType); var result = UserDefinedConversionResult.Valid(ImmutableArray.Create(analysis), 0); return new Conversion(result, isImplicit); } diff --git a/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs index e853e3c1ba206..a28f91e5d95cc 100644 --- a/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs @@ -203,6 +203,7 @@ private Conversion RewriteConversion(Conversion conversion) { case ConversionKind.ExplicitUserDefined: case ConversionKind.ImplicitUserDefined: + Debug.Assert(conversion.ConstrainedToTypeOpt is null); return new Conversion(conversion.Kind, VisitMethodSymbol(conversion.Method), conversion.IsExtensionMethod); case ConversionKind.MethodGroup: throw ExceptionUtilities.UnexpectedValue(conversion.Kind); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index 1870bcf8fb4f5..bb4517f5ed18f 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -23984,5 +23984,1108 @@ class C2 : I1 Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "I1").WithArguments("I1", "T", "C2").WithLocation(14, 21) ); } + + [Theory] + [CombinatorialData] + public void ConsumeAbstractConversionOperator_01([CombinatorialValues("implicit", "explicit")] string op) + { + string cast = (op == "explicit" ? "(int)" : ""); + + var source1 = +@" +interface I1 where T : I1 +{ + abstract static " + op + @" operator int(T x); + + static int M02(I1 x) + { + return " + cast + @"x; + } + + int M03(I1 y) + { + return " + cast + @"y; + } +} + +class Test where T : I1 +{ + static int MT1(I1 a) + { + return " + cast + @"a; + } + + static void MT2() + { + _ = (System.Linq.Expressions.Expression>)((T b) => " + cast + @"b); + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var error = (op == "explicit" ? ErrorCode.ERR_NoExplicitConv : ErrorCode.ERR_NoImplicitConv); + + compilation1.VerifyDiagnostics( + // (8,16): error CS0030: Cannot convert type 'I1' to 'int' + // return (int)x; + Diagnostic(error, cast + "x").WithArguments("I1", "int").WithLocation(8, 16), + // (13,16): error CS0030: Cannot convert type 'I1' to 'int' + // return (int)y; + Diagnostic(error, cast + "y").WithArguments("I1", "int").WithLocation(13, 16), + // (21,16): error CS0030: Cannot convert type 'I1' to 'int' + // return (int)a; + Diagnostic(error, cast + "a").WithArguments("I1", "int").WithLocation(21, 16), + // (26,80): error CS9108: An expression tree may not contain an access of static abstract interface member + // _ = (System.Linq.Expressions.Expression>)((T b) => (int)b); + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, cast + "b").WithLocation(26, 80) + ); + } + + [Theory] + [CombinatorialData] + public void ConsumeAbstractConversionOperator_03([CombinatorialValues("implicit", "explicit")] string op) + { + string metadataName = ConversionOperatorName(op); + bool needCast = op == "explicit"; + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator int(T x); +} + +class Test +{ + static int M02(T x) where T : U where U : I1 + { + return " + (needCast ? "(int)" : "") + @"x; + } + + static int? M03(T y) where T : U where U : I1 + { + return " + (needCast ? "(int?)" : "") + @"y; + } + + static int? M04(T? y) where T : struct, U where U : I1 + { + return " + (needCast ? "(int?)" : "") + @"y; + } + + static int? M05() where T : struct, U where U : I1 + { + return " + (needCast ? "(int?)" : "") + @"(T?)new T(); + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02(T)", +@" +{ + // Code size 18 (0x12) + .maxstack 1 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: constrained. ""T"" + IL_0008: call ""int I1." + metadataName + @"(T)"" + IL_000d: stloc.0 + IL_000e: br.s IL_0010 + IL_0010: ldloc.0 + IL_0011: ret +} +"); + verifier.VerifyIL("Test.M03(T)", +@" +{ + // Code size 23 (0x17) + .maxstack 1 + .locals init (int? V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: constrained. ""T"" + IL_0008: call ""int I1." + metadataName + @"(T)"" + IL_000d: newobj ""int?..ctor(int)"" + IL_0012: stloc.0 + IL_0013: br.s IL_0015 + IL_0015: ldloc.0 + IL_0016: ret +} +"); + verifier.VerifyIL("Test.M04(T?)", +@" +{ + // Code size 51 (0x33) + .maxstack 1 + .locals init (T? V_0, + int? V_1, + int? V_2) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloca.s V_0 + IL_0005: call ""readonly bool T?.HasValue.get"" + IL_000a: brtrue.s IL_0017 + IL_000c: ldloca.s V_1 + IL_000e: initobj ""int?"" + IL_0014: ldloc.1 + IL_0015: br.s IL_002e + IL_0017: ldloca.s V_0 + IL_0019: call ""readonly T T?.GetValueOrDefault()"" + IL_001e: constrained. ""T"" + IL_0024: call ""int I1." + metadataName + @"(T)"" + IL_0029: newobj ""int?..ctor(int)"" + IL_002e: stloc.2 + IL_002f: br.s IL_0031 + IL_0031: ldloc.2 + IL_0032: ret +} +"); + verifier.VerifyIL("Test.M05()", +@" +{ + // Code size 27 (0x1b) + .maxstack 1 + .locals init (int? V_0) + IL_0000: nop + IL_0001: call ""T System.Activator.CreateInstance()"" + IL_0006: constrained. ""T"" + IL_000c: call ""int I1." + metadataName + @"(T)"" + IL_0011: newobj ""int?..ctor(int)"" + IL_0016: stloc.0 + IL_0017: br.s IL_0019 + IL_0019: ldloc.0 + IL_001a: ret +} +"); + + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02(T)", +@" +{ + // Code size 13 (0xd) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: constrained. ""T"" + IL_0007: call ""int I1." + metadataName + @"(T)"" + IL_000c: ret +} +"); + verifier.VerifyIL("Test.M03(T)", +@" +{ + // Code size 18 (0x12) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: constrained. ""T"" + IL_0007: call ""int I1." + metadataName + @"(T)"" + IL_000c: newobj ""int?..ctor(int)"" + IL_0011: ret +} +"); + verifier.VerifyIL("Test.M04(T?)", +@" +{ + // Code size 45 (0x2d) + .maxstack 1 + .locals init (T? V_0, + int? V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""readonly bool T?.HasValue.get"" + IL_0009: brtrue.s IL_0015 + IL_000b: ldloca.s V_1 + IL_000d: initobj ""int?"" + IL_0013: ldloc.1 + IL_0014: ret + IL_0015: ldloca.s V_0 + IL_0017: call ""readonly T T?.GetValueOrDefault()"" + IL_001c: constrained. ""T"" + IL_0022: call ""int I1." + metadataName + @"(T)"" + IL_0027: newobj ""int?..ctor(int)"" + IL_002c: ret +} +"); + verifier.VerifyIL("Test.M05()", +@" +{ + // Code size 22 (0x16) + .maxstack 1 + IL_0000: call ""T System.Activator.CreateInstance()"" + IL_0005: constrained. ""T"" + IL_000b: call ""int I1." + metadataName + @"(T)"" + IL_0010: newobj ""int?..ctor(int)"" + IL_0015: ret +} +"); + + var tree = compilation1.SyntaxTrees.Single(); + var model = compilation1.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().First(); + + Assert.Equal("return " + (needCast ? "(int)" : "") + @"x;", node.ToString()); + + VerifyOperationTreeForNode(compilation1, model, node, +// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" constraint is important for this operator, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? +@" +IReturnOperation (OperationKind.Return, Type: null) (Syntax: 'return " + (needCast ? "(int)" : "") + @"x;') + ReturnedValue: + IConversionOperation (TryCast: False, Unchecked) (OperatorMethod: System.Int32 I1." + metadataName + @"(T x)) (OperationKind.Conversion, Type: System.Int32" + (needCast ? "" : ", IsImplicit") + @") (Syntax: '" + (needCast ? "(int)" : "") + @"x') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: True) (MethodSymbol: System.Int32 I1." + metadataName + @"(T x)) + Operand: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T) (Syntax: 'x') +"); + } + + [Theory] + [CombinatorialData] + public void ConsumeAbstractConversionOperator_04([CombinatorialValues("implicit", "explicit")] string op) + { + bool needCast = op == "explicit"; + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator int(T x); +} +"; + var source2 = +@" +class Test +{ + static int M02(T x) where T : I1 + { + return " + (needCast ? "(int)" : "") + @"x; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (6,16): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // return (int)x; + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, (needCast ? "(int)" : "") + "x").WithLocation(6, 16) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation3.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + // (12,39): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static explicit operator int(T x); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "int").WithLocation(12, 39) + ); + } + + [Theory] + [CombinatorialData] + public void ConsumeAbstractConversionOperator_06([CombinatorialValues("implicit", "explicit")] string op) + { + bool needCast = op == "explicit"; + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator int(T x); +} +"; + var source2 = +@" +class Test +{ + static int M02(T x) where T : I1 + { + return " + (needCast ? "(int)" : "") + @"x; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (6,16): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // return x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, (needCast ? "(int)" : "") + "x").WithArguments("static abstract members in interfaces").WithLocation(6, 16) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation3.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( + // (12,39): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static implicit operator int(T x); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "int").WithArguments("abstract", "9.0", "preview").WithLocation(12, 39) + ); + } + + [Theory] + [CombinatorialData] + public void ConsumeAbstractConversionOperator_07([CombinatorialValues("implicit", "explicit")] string op) + { + // Same as ConsumeAbstractConversionOperator_03 only direction of conversion is flipped + + string metadataName = ConversionOperatorName(op); + bool needCast = op == "explicit"; + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator T(int x); +} + +class Test +{ + static T M02(int x) where T : U where U : I1 + { + return " + (needCast ? "(T)" : "") + @"x; + } + + static T? M03(int y) where T : struct, U where U : I1 + { + return " + (needCast ? "(T?)" : "") + @"y; + } + + static T? M04(int? y) where T : struct, U where U : I1 + { + return " + (needCast ? "(T?)" : "") + @"y; + } + + static T? M05() where T : struct, U where U : I1 + { + return " + (needCast ? "(T?)" : "") + @"(T?)0; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02(int)", +@" +{ + // Code size 18 (0x12) + .maxstack 1 + .locals init (T V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: constrained. ""T"" + IL_0008: call ""T I1." + metadataName + @"(int)"" + IL_000d: stloc.0 + IL_000e: br.s IL_0010 + IL_0010: ldloc.0 + IL_0011: ret +} +"); + verifier.VerifyIL("Test.M03(int)", +@" +{ + // Code size 23 (0x17) + .maxstack 1 + .locals init (T? V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: constrained. ""T"" + IL_0008: call ""T I1." + metadataName + @"(int)"" + IL_000d: newobj ""T?..ctor(T)"" + IL_0012: stloc.0 + IL_0013: br.s IL_0015 + IL_0015: ldloc.0 + IL_0016: ret +} +"); + verifier.VerifyIL("Test.M04(int?)", +@" +{ + // Code size 51 (0x33) + .maxstack 1 + .locals init (int? V_0, + T? V_1, + T? V_2) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloca.s V_0 + IL_0005: call ""readonly bool int?.HasValue.get"" + IL_000a: brtrue.s IL_0017 + IL_000c: ldloca.s V_1 + IL_000e: initobj ""T?"" + IL_0014: ldloc.1 + IL_0015: br.s IL_002e + IL_0017: ldloca.s V_0 + IL_0019: call ""readonly int int?.GetValueOrDefault()"" + IL_001e: constrained. ""T"" + IL_0024: call ""T I1." + metadataName + @"(int)"" + IL_0029: newobj ""T?..ctor(T)"" + IL_002e: stloc.2 + IL_002f: br.s IL_0031 + IL_0031: ldloc.2 + IL_0032: ret +} +"); + verifier.VerifyIL("Test.M05()", +@" +{ + // Code size 23 (0x17) + .maxstack 1 + .locals init (T? V_0) + IL_0000: nop + IL_0001: ldc.i4.0 + IL_0002: constrained. ""T"" + IL_0008: call ""T I1." + metadataName + @"(int)"" + IL_000d: newobj ""T?..ctor(T)"" + IL_0012: stloc.0 + IL_0013: br.s IL_0015 + IL_0015: ldloc.0 + IL_0016: ret +} +"); + + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02(int)", +@" +{ + // Code size 13 (0xd) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: constrained. ""T"" + IL_0007: call ""T I1." + metadataName + @"(int)"" + IL_000c: ret +} +"); + verifier.VerifyIL("Test.M03(int)", +@" +{ + // Code size 18 (0x12) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: constrained. ""T"" + IL_0007: call ""T I1." + metadataName + @"(int)"" + IL_000c: newobj ""T?..ctor(T)"" + IL_0011: ret +} +"); + verifier.VerifyIL("Test.M04(int?)", +@" +{ + // Code size 45 (0x2d) + .maxstack 1 + .locals init (int? V_0, + T? V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""readonly bool int?.HasValue.get"" + IL_0009: brtrue.s IL_0015 + IL_000b: ldloca.s V_1 + IL_000d: initobj ""T?"" + IL_0013: ldloc.1 + IL_0014: ret + IL_0015: ldloca.s V_0 + IL_0017: call ""readonly int int?.GetValueOrDefault()"" + IL_001c: constrained. ""T"" + IL_0022: call ""T I1." + metadataName + @"(int)"" + IL_0027: newobj ""T?..ctor(T)"" + IL_002c: ret +} +"); + verifier.VerifyIL("Test.M05()", +@" +{ + // Code size 18 (0x12) + .maxstack 1 + IL_0000: ldc.i4.0 + IL_0001: constrained. ""T"" + IL_0007: call ""T I1." + metadataName + @"(int)"" + IL_000c: newobj ""T?..ctor(T)"" + IL_0011: ret +} +"); + + var tree = compilation1.SyntaxTrees.Single(); + var model = compilation1.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().First(); + + Assert.Equal("return " + (needCast ? "(T)" : "") + @"x;", node.ToString()); + + VerifyOperationTreeForNode(compilation1, model, node, +// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" constraint is important for this operator, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? +@" +IReturnOperation (OperationKind.Return, Type: null) (Syntax: 'return " + (needCast ? "(T)" : "") + @"x;') + ReturnedValue: + IConversionOperation (TryCast: False, Unchecked) (OperatorMethod: T I1." + metadataName + @"(System.Int32 x)) (OperationKind.Conversion, Type: T" + (needCast ? "" : ", IsImplicit") + @") (Syntax: '" + (needCast ? "(T)" : "") + @"x') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: True) (MethodSymbol: T I1." + metadataName + @"(System.Int32 x)) + Operand: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') +"); + } + + [Theory] + [CombinatorialData] + public void ConsumeAbstractConversionOperator_08([CombinatorialValues("implicit", "explicit")] string op) + { + // Don't look in interfaces of the effective base + + string metadataName = ConversionOperatorName(op); + bool needCast = op == "explicit"; + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator int(T x); +} + +class C1 : I1> +{ + static " + op + @" I1>.operator int(C1 x) => default; +} + +class Test +{ + static int M02(T x) where T : U where U : C1 + { + return " + (needCast ? "(int)" : "") + @"x; + } + + static int M03(C1 y) where T : I1> + { + return " + (needCast ? "(int)" : "") + @"y; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var error = (op == "explicit" ? ErrorCode.ERR_NoExplicitConv : ErrorCode.ERR_NoImplicitConv); + + compilation1.VerifyDiagnostics( + // (16,16): error CS0030: Cannot convert type 'T' to 'int' + // return (int)x; + Diagnostic(error, (needCast ? "(int)" : "") + "x").WithArguments("T", "int").WithLocation(16, 16), + // (21,16): error CS0030: Cannot convert type 'C1' to 'int' + // return (int)y; + Diagnostic(error, (needCast ? "(int)" : "") + "y").WithArguments("C1", "int").WithLocation(21, 16) + ); + } + + [Theory] + [CombinatorialData] + public void ConsumeAbstractConversionOperator_09([CombinatorialValues("implicit", "explicit")] string op) + { + // Same as ConsumeAbstractConversionOperator_08 only direction of conversion is flipped + + string metadataName = ConversionOperatorName(op); + bool needCast = op == "explicit"; + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator T(int x); +} + +class C1 : I1> +{ + static " + op + @" I1>.operator C1(int x) => default; +} + +class Test +{ + static T M02(int x) where T : U where U : C1 + { + return " + (needCast ? "(T)" : "") + @"x; + } + + static C1 M03(int y) where T : I1> + { + return " + (needCast ? "(C1)" : "") + @"y; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var error = (op == "explicit" ? ErrorCode.ERR_NoExplicitConv : ErrorCode.ERR_NoImplicitConv); + + compilation1.VerifyDiagnostics( + // (16,16): error CS0030: Cannot convert type 'int' to 'T' + // return (T)x; + Diagnostic(error, (needCast ? "(T)" : "") + "x").WithArguments("int", "T").WithLocation(16, 16), + // (21,16): error CS0030: Cannot convert type 'int' to 'C1' + // return (C1)y; + Diagnostic(error, (needCast ? "(C1)" : "") + "y").WithArguments("int", "C1").WithLocation(21, 16) + ); + } + + [Theory] + [CombinatorialData] + public void ConsumeAbstractConversionOperator_10([CombinatorialValues("implicit", "explicit")] string op) + { + // Look in derived interfaces + + string metadataName = ConversionOperatorName(op); + bool needCast = op == "explicit"; + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator int(T x); +} + +public interface I2 : I1 where T : I1 +{} + +class Test +{ + static int M02(T x) where T : U where U : I2 + { + return " + (needCast ? "(int)" : "") + @"x; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02(T)", +@" +{ + // Code size 18 (0x12) + .maxstack 1 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: constrained. ""T"" + IL_0008: call ""int I1." + metadataName + @"(T)"" + IL_000d: stloc.0 + IL_000e: br.s IL_0010 + IL_0010: ldloc.0 + IL_0011: ret +} +"); + } + + [Theory] + [CombinatorialData] + public void ConsumeAbstractConversionOperator_11([CombinatorialValues("implicit", "explicit")] string op) + { + // Same as ConsumeAbstractConversionOperator_10 only direction of conversion is flipped + + string metadataName = ConversionOperatorName(op); + bool needCast = op == "explicit"; + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator T(int x); +} + +public interface I2 : I1 where T : I1 +{} + +class Test +{ + static T M02(int x) where T : U where U : I2 + { + return " + (needCast ? "(T)" : "") + @"x; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02(int)", +@" +{ + // Code size 18 (0x12) + .maxstack 1 + .locals init (T V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: constrained. ""T"" + IL_0008: call ""T I1." + metadataName + @"(int)"" + IL_000d: stloc.0 + IL_000e: br.s IL_0010 + IL_0010: ldloc.0 + IL_0011: ret +} +"); + } + + [Theory] + [CombinatorialData] + public void ConsumeAbstractConversionOperator_12([CombinatorialValues("implicit", "explicit")] string op) + { + // Ignore duplicate candidates + + string metadataName = ConversionOperatorName(op); + bool needCast = op == "explicit"; + + var source1 = +@" +public interface I1 where T : I1 where U : I1 +{ + abstract static " + op + @" operator U(T x); +} + +class Test +{ + static U M02(T x) where T : I1 where U : I1 + { + return " + (needCast ? "(U)" : "") + @"x; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02(T)", +@" +{ + // Code size 18 (0x12) + .maxstack 1 + .locals init (U V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: constrained. ""T"" + IL_0008: call ""U I1." + metadataName + @"(T)"" + IL_000d: stloc.0 + IL_000e: br.s IL_0010 + IL_0010: ldloc.0 + IL_0011: ret +} +"); + } + + [Theory] + [CombinatorialData] + public void ConsumeAbstractConversionOperator_13([CombinatorialValues("implicit", "explicit")] string op) + { + // Look in effective base + + string metadataName = ConversionOperatorName(op); + bool needCast = op == "explicit"; + + var source1 = +@" +public class C1 +{ + public static " + op + @" operator int(C1 x) => default; +} + +class Test +{ + static int M02(T x) where T : U where U : C1 + { + return " + (needCast ? "(int)" : "") + @"x; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02(T)", +@" +{ + // Code size 17 (0x11) + .maxstack 1 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: box ""T"" + IL_0007: call ""int C1." + metadataName + @"(C1)"" + IL_000c: stloc.0 + IL_000d: br.s IL_000f + IL_000f: ldloc.0 + IL_0010: ret +} +"); + } + + [Fact] + public void ConsumeAbstractConversionOperator_14() + { + // Same as ConsumeAbstractConversionOperator_13 only direction of conversion is flipped + var source1 = +@" +public class C1 +{ + public static explicit operator C1(int x) => default; +} + +class Test +{ + static T M02(int x) where T : U where U : C1 + { + return (T)x; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02(int)", +@" +{ + // Code size 17 (0x11) + .maxstack 1 + .locals init (T V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""C1 C1.op_Explicit(int)"" + IL_0007: unbox.any ""T"" + IL_000c: stloc.0 + IL_000d: br.s IL_000f + IL_000f: ldloc.0 + IL_0010: ret +} +"); + } + + [Theory] + [CombinatorialData] + public void ConsumeAbstractConversionOperator_15([CombinatorialValues("implicit", "explicit")] string op) + { + // If there is a non-trivial class constraint, interfaces are not looked at. + + string metadataName = ConversionOperatorName(op); + bool needCast = op == "explicit"; + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator int(T x); +} + +public class C1 : I1 +{ + public static " + op + @" operator int(C1 x) => default; +} + +class Test +{ + static int M02(T x) where T : U where U : C1, I1 + { + return " + (needCast ? "(int)" : "") + @"x; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02(T)", +@" +{ + // Code size 17 (0x11) + .maxstack 1 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: box ""T"" + IL_0007: call ""int C1." + metadataName + @"(C1)"" + IL_000c: stloc.0 + IL_000d: br.s IL_000f + IL_000f: ldloc.0 + IL_0010: ret +} +"); + } + + [Fact] + public void ConsumeAbstractConversionOperator_16() + { + // Same as ConsumeAbstractConversionOperator_15 only direction of conversion is flipped + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static explicit operator T(int x); +} + +public class C1 : I1 +{ + public static explicit operator C1(int x) => default; +} + +class Test +{ + static T M02(int x) where T : U where U : C1, I1 + { + return (T)x; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Test.M02(int)", +@" +{ + // Code size 17 (0x11) + .maxstack 1 + .locals init (T V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""C1 C1.op_Explicit(int)"" + IL_0007: unbox.any ""T"" + IL_000c: stloc.0 + IL_000d: br.s IL_000f + IL_000f: ldloc.0 + IL_0010: ret +} +"); + } + + [Theory] + [CombinatorialData] + public void ConsumeAbstractConversionOperator_17([CombinatorialValues("implicit", "explicit")] string op) + { + // If there is a non-trivial class constraint, interfaces are not looked at. + + bool needCast = op == "explicit"; + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator int(T x); +} + +public class C1 +{ +} + +class Test +{ + static int M02(T x) where T : U where U : C1, I1 + { + return " + (needCast ? "(int)" : "") + @"x; + } + + static int M03(T y) where T : U where U : I1 + { + return " + (needCast ? "(int)" : "") + @"y; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + compilation1.VerifyDiagnostics( + // (15,16): error CS0030: Cannot convert type 'T' to 'int' + // return (int)x; + Diagnostic((op == "explicit" ? ErrorCode.ERR_NoExplicitConv : ErrorCode.ERR_NoImplicitConv), (needCast ? "(int)" : "") + "x").WithArguments("T", "int").WithLocation(15, 16) + ); + } + + [Theory] + [CombinatorialData] + public void ConsumeAbstractConversionOperator_18([CombinatorialValues("implicit", "explicit")] string op) + { + // Same as ConsumeAbstractConversionOperator_17 only direction of conversion is flipped + + bool needCast = op == "explicit"; + + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static " + op + @" operator T(int x); +} + +public class C1 +{ +} + +class Test +{ + static T M02(int x) where T : U where U : C1, I1 + { + return " + (needCast ? "(T)" : "") + @"x; + } + + static T M03(int y) where T : U where U : I1 + { + return " + (needCast ? "(T)" : "") + @"y; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + compilation1.VerifyDiagnostics( + // (15,16): error CS0030: Cannot convert type 'int' to 'T' + // return (T)x; + Diagnostic((op == "explicit" ? ErrorCode.ERR_NoExplicitConv : ErrorCode.ERR_NoImplicitConv), (needCast ? "(T)" : "") + "x").WithArguments("int", "T").WithLocation(15, 16) + ); + } } } From 56c6b4bb60a8d790703042b1c76b83f79bc6dd58 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Wed, 2 Jun 2021 18:31:21 -0700 Subject: [PATCH 089/127] Remove PROTOTYPE comments StaticAbstractMembersInInterfaces feature branch. (#53804) Related to https://github.com/dotnet/csharplang/pull/4794. --- .../CSharp/Portable/Binder/Binder_Query.cs | 2 +- .../Portable/Binder/Binder_TupleOperators.cs | 2 +- .../Conversions/UserDefinedConversions.cs | 2 +- .../LocalRewriter_TupleBinaryOperator.cs | 4 +- .../CSharp/Portable/Symbols/AssemblySymbol.cs | 2 +- .../SourceUserDefinedOperatorSymbolBase.cs | 4 +- .../CSharp/Portable/Symbols/TypeSymbol.cs | 2 +- .../StaticAbstractMembersInInterfacesTests.cs | 107 +++++++++--------- 8 files changed, 63 insertions(+), 62 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs index cec4b3a6f2145..38c543310628c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs @@ -780,7 +780,7 @@ protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression r { if (ultimateReceiver.Type.TypeKind == TypeKind.TypeParameter) { - // PROTOTYPE(StaticAbstractMembersInInterfaces): Do we really want to enable usage of static abstract members here? + // https://github.com/dotnet/roslyn/issues/53796: Do we really want to enable usage of static abstract members here? Error(diagnostics, ErrorCode.ERR_BadSKunknown, ultimateReceiver.Syntax, ultimateReceiver.Type, MessageID.IDS_SK_TYVAR.Localize()); } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs index 1eca4037574cf..ab0ca36302020 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs @@ -104,7 +104,7 @@ private TupleBinaryOperatorInfo BindTupleBinaryOperatorInfo(BinaryExpressionSynt case BoundBinaryOperator binary: PrepareBoolConversionAndTruthOperator(binary.Type, node, kind, diagnostics, out Conversion conversionIntoBoolOperator, out UnaryOperatorSignature boolOperator); - // PROTOTYPE(StaticAbstractMembersInInterfaces): Ensure we have a unit-test for this code path. + // https://github.com/dotnet/roslyn/issues/53797: Ensure we have a unit-test for this code path. return new TupleBinaryOperatorInfo.Single(binary.Left.Type, binary.Right.Type, binary.OperatorKind, binary.MethodOpt, binary.ConstrainedToTypeOpt, conversionIntoBoolOperator, boolOperator); default: diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedConversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedConversions.cs index 0ab8f38f580d3..6c28f576c6119 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedConversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedConversions.cs @@ -37,7 +37,7 @@ public static void AddTypesParticipatingInUserDefinedConversion(ArrayBuilder internal bool RuntimeSupportsStaticAbstractMembersInInterfaces { - // PROTOTYPE(StaticAbstractMembersInInterfaces): Implement the actual check, this is a temporary stub. + // https://github.com/dotnet/roslyn/issues/53800: Implement the actual check, this is a temporary stub. get => RuntimeSupportsDefaultInterfaceImplementation; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs index 37ac564c1aae2..09581a5bd0373 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs @@ -653,9 +653,9 @@ public static bool IsSelfConstrainedTypeParameter(TypeSymbol type, NamedTypeSymb { Debug.Assert(containingType.IsDefinition); return type is TypeParameterSymbol p && - // PROTOTYPE(StaticAbstractMembersInInterfaces): For now assuming the type parameter must belong to the containing type. + // https://github.com/dotnet/roslyn/issues/53801: For now assuming the type parameter must belong to the containing type. (object)p.ContainingSymbol == containingType && - // PROTOTYPE(StaticAbstractMembersInInterfaces): For now assume containing type must be one of the directly specified constraints. + // https://github.com/dotnet/roslyn/issues/53801: For now assume containing type must be one of the directly specified constraints. p.ConstraintTypesNoUseSiteDiagnostics.Any((typeArgument, containingType) => typeArgument.Type.Equals(containingType, ComparisonForUserDefinedOperators), containingType); } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs index 0d4ccc122bc2e..96b437d9c5425 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs @@ -1691,7 +1691,7 @@ private static void ReportImplicitImplementationMatchDiagnostics(Symbol interfac { //do nothing - not an ambiguous implementation - // PROTOTYPE(StaticAbstractMembersInInterfaces): We likely need to do something here for static members. + // https://github.com/dotnet/roslyn/issues/53802: We likely need to do something here for static members. } else if (MemberSignatureComparer.RuntimeImplicitImplementationComparer.Equals(interfaceMember, member) && !member.IsAccessor()) { diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index bb4517f5ed18f..032925be05ba8 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -6366,9 +6366,9 @@ .maxstack 1 Assert.Equal("T.M01()", node.ToString()); VerifyOperationTreeForNode(compilation1, model, node, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" qualifier is important for this invocation, but it is not -// reflected in the IOperation tree. Should we change the shape of the tree in order -// to expose this information? +// https://github.com/dotnet/roslyn/issues/53803: It feels like the "T" qualifier is important for this invocation, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? @" IInvocationOperation (virtual void I1.M01()) (OperationKind.Invocation, Type: System.Void) (Syntax: 'T.M01()') Instance Receiver: @@ -6450,6 +6450,7 @@ static void M02() where T : I1 parseOptions: TestOptions.RegularPreview, targetFramework: TargetFramework.NetCoreApp); + // https://github.com/dotnet/roslyn/issues/53796: Confirm whether we want to enable the 'from t in T' scenario. compilation1.VerifyDiagnostics( // (11,23): error CS0119: 'T' is a type parameter, which is not valid in the given context // _ = from t in T select t + 1; @@ -6915,9 +6916,9 @@ .locals init (T? V_0, case ("", "++"): case ("", "--"): VerifyOperationTreeForNode(compilation1, model, node, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" constraint is important for this operator, but it is not -// reflected in the IOperation tree. Should we change the shape of the tree in order -// to expose this information? +// https://github.com/dotnet/roslyn/issues/53803: It feels like the "T" constraint is important for this operator, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? @" IIncrementOrDecrementOperation (" + (prefixOp != "" ? "Prefix" : "Postfix") + @") (OperatorMethod: T I1." + metadataName + @"(T x)) (OperationKind." + opKind + @", Type: T) (Syntax: '" + prefixOp + "x" + postfixOp + @"') Target: @@ -6927,9 +6928,9 @@ .locals init (T? V_0, default: VerifyOperationTreeForNode(compilation1, model, node, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" constraint is important for this operator, but it is not -// reflected in the IOperation tree. Should we change the shape of the tree in order -// to expose this information? +// https://github.com/dotnet/roslyn/issues/53803: It feels like the "T" constraint is important for this operator, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? @" IUnaryOperation (UnaryOperatorKind." + opKind + @") (OperatorMethod: T I1." + metadataName + @"(T x)) (OperationKind.Unary, Type: T) (Syntax: '" + prefixOp + "x" + postfixOp + @"') Operand: @@ -7166,9 +7167,9 @@ .maxstack 1 Assert.Equal("x ? true : false", node.ToString()); VerifyOperationTreeForNode(compilation1, model, node, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" constraint is important for this operator, but it is not -// reflected in the IOperation tree. Should we change the shape of the tree in order -// to expose this information? +// https://github.com/dotnet/roslyn/issues/53803: It feels like the "T" constraint is important for this operator, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? @" IConditionalOperation (OperationKind.Conditional, Type: System.Boolean) (Syntax: 'x ? true : false') Condition: @@ -7983,9 +7984,9 @@ .locals init (T? V_0) Assert.Equal("x " + op + " 1", node.ToString()); VerifyOperationTreeForNode(compilation1, model, node, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" constraint is important for this operator, but it is not -// reflected in the IOperation tree. Should we change the shape of the tree in order -// to expose this information? +// https://github.com/dotnet/roslyn/issues/53803: It feels like the "T" constraint is important for this operator, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? @" IBinaryOperation (BinaryOperatorKind." + BinaryOperatorKind(op) + @") (OperatorMethod: T I1." + metadataName + @"(T x, System.Int32 a)) (OperationKind.Binary, Type: T) (Syntax: 'x " + op + @" 1') Left: @@ -8178,9 +8179,9 @@ .maxstack 2 Assert.Equal("x " + op + " 1", node.ToString()); VerifyOperationTreeForNode(compilation1, model, node, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" constraint is important for this operator, but it is not -// reflected in the IOperation tree. Should we change the shape of the tree in order -// to expose this information? +// https://github.com/dotnet/roslyn/issues/53803: It feels like the "T" constraint is important for this operator, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? @" IBinaryOperation (BinaryOperatorKind." + BinaryOperatorKind(op) + @") (OperatorMethod: System.Boolean I1." + metadataName + @"(T x, System.Int32 a)) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'x " + op + @" 1') Left: @@ -8370,9 +8371,9 @@ .locals init (T? V_0, Assert.Equal("x " + op + " y", node.ToString()); VerifyOperationTreeForNode(compilation1, model, node, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" constraint is important for this operator, but it is not -// reflected in the IOperation tree. Should we change the shape of the tree in order -// to expose this information? +// https://github.com/dotnet/roslyn/issues/53803: It feels like the "T" constraint is important for this operator, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? @" IBinaryOperation (BinaryOperatorKind." + BinaryOperatorKind(op) + @", IsLifted) (OperatorMethod: System.Boolean I1." + metadataName + @"(T x, T a)) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'x " + op + @" y') Left: @@ -8567,9 +8568,9 @@ .locals init (T? V_0, Assert.Equal("x " + op + op + " y", node1.ToString()); VerifyOperationTreeForNode(compilation1, model, node1, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" constraint is important for this operator, but it is not -// reflected in the IOperation tree. Should we change the shape of the tree in order -// to expose this information? +// https://github.com/dotnet/roslyn/issues/53803: It feels like the "T" constraint is important for this operator, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? @" IBinaryOperation (BinaryOperatorKind." + opKind + @") (OperatorMethod: T I1." + binaryMetadataName + @"(T a, T x)) (OperationKind.Binary, Type: T) (Syntax: 'x " + op + op + @" y') Left: @@ -8824,9 +8825,9 @@ .locals init (I1 V_0) Assert.Equal("x " + op + op + " y", node1.ToString()); VerifyOperationTreeForNode(compilation1, model, node1, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" constraint is important for this operator, but it is not -// reflected in the IOperation tree. Should we change the shape of the tree in order -// to expose this information? +// https://github.com/dotnet/roslyn/issues/53803: It feels like the "T" constraint is important for this operator, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? @" IBinaryOperation (BinaryOperatorKind." + opKind + @") (OperatorMethod: I1 I1." + binaryMetadataName + @"(I1 a, I1 x)) (OperationKind.Binary, Type: I1) (Syntax: 'x " + op + op + @" y') Left: @@ -9176,7 +9177,7 @@ .locals init (T? V_0, Assert.Equal("x " + op + "= 1", node.ToString()); VerifyOperationTreeForNode(compilation1, model, node, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" constraint is important for this operator, but it is not +// https://github.com/dotnet/roslyn/issues/53803: It feels like the "T" constraint is important for this operator, but it is not // reflected in the IOperation tree. Should we change the shape of the tree in order // to expose this information? @" @@ -9986,9 +9987,9 @@ .maxstack 1 Assert.Equal("T.P01", node.ToString()); VerifyOperationTreeForNode(compilation1, model, node, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" qualifier is important for this invocation, but it is not -// reflected in the IOperation tree. Should we change the shape of the tree in order -// to expose this information? +// https://github.com/dotnet/roslyn/issues/53803: It feels like the "T" qualifier is important for this invocation, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? @" IPropertyReferenceOperation: System.Int32 I1.P01 { get; set; } (Static) (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'T.P01') Instance Receiver: @@ -10062,9 +10063,9 @@ .maxstack 1 Assert.Equal("T.P01", node.ToString()); VerifyOperationTreeForNode(compilation1, model, node, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" qualifier is important for this invocation, but it is not -// reflected in the IOperation tree. Should we change the shape of the tree in order -// to expose this information? +// https://github.com/dotnet/roslyn/issues/53803: It feels like the "T" qualifier is important for this invocation, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? @" IPropertyReferenceOperation: System.Int32 I1.P01 { get; set; } (Static) (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'T.P01') Instance Receiver: @@ -10174,9 +10175,9 @@ .maxstack 1 Assert.Equal("T.P01", node.ToString()); VerifyOperationTreeForNode(compilation1, model, node, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" qualifier is important for this invocation, but it is not -// reflected in the IOperation tree. Should we change the shape of the tree in order -// to expose this information? +// https://github.com/dotnet/roslyn/issues/53803: It feels like the "T" qualifier is important for this invocation, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? @" IPropertyReferenceOperation: System.Int32 I1.P01 { get; set; } (Static) (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'T.P01') Instance Receiver: @@ -10850,9 +10851,9 @@ .maxstack 1 Assert.Equal("T.E01", node.ToString()); VerifyOperationTreeForNode(compilation1, model, node, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" qualifier is important for this invocation, but it is not -// reflected in the IOperation tree. Should we change the shape of the tree in order -// to expose this information? +// https://github.com/dotnet/roslyn/issues/53803: It feels like the "T" qualifier is important for this invocation, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? @" IEventReferenceOperation: event System.Action I1.E01 (Static) (OperationKind.EventReference, Type: System.Action) (Syntax: 'T.E01') Instance Receiver: @@ -11405,9 +11406,9 @@ .maxstack 2 Assert.Equal("T.M01", node.ToString()); VerifyOperationTreeForNode(compilation1, model, node, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" qualifier is important for this invocation, but it is not -// reflected in the IOperation tree. Should we change the shape of the tree in order -// to expose this information? +// https://github.com/dotnet/roslyn/issues/53803: It feels like the "T" qualifier is important for this invocation, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? @" IMethodReferenceOperation: void I1.M01() (IsVirtual) (Static) (OperationKind.MethodReference, Type: null) (Syntax: 'T.M01') Instance Receiver: @@ -11665,9 +11666,9 @@ .maxstack 2 Assert.Equal("T.M01", node.ToString()); VerifyOperationTreeForNode(compilation1, model, node, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" qualifier is important for this invocation, but it is not -// reflected in the IOperation tree. Should we change the shape of the tree in order -// to expose this information? +// https://github.com/dotnet/roslyn/issues/53803: It feels like the "T" qualifier is important for this invocation, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? @" IMethodReferenceOperation: void I1.M01() (IsVirtual) (Static) (OperationKind.MethodReference, Type: null) (Syntax: 'T.M01') Instance Receiver: @@ -11924,9 +11925,9 @@ .maxstack 1 Assert.Equal("T.M01", node.ToString()); VerifyOperationTreeForNode(compilation1, model, node, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" qualifier is important for this invocation, but it is not -// reflected in the IOperation tree. Should we change the shape of the tree in order -// to expose this information? +// https://github.com/dotnet/roslyn/issues/53803: It feels like the "T" qualifier is important for this invocation, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? @" IMethodReferenceOperation: void I1.M01() (IsVirtual) (Static) (OperationKind.MethodReference, Type: null) (Syntax: 'T.M01') Instance Receiver: @@ -24239,9 +24240,9 @@ .maxstack 1 Assert.Equal("return " + (needCast ? "(int)" : "") + @"x;", node.ToString()); VerifyOperationTreeForNode(compilation1, model, node, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" constraint is important for this operator, but it is not -// reflected in the IOperation tree. Should we change the shape of the tree in order -// to expose this information? +// https://github.com/dotnet/roslyn/issues/53803: It feels like the "T" constraint is important for this operator, but it is not +// reflected in the IOperation tree. Should we change the shape of the tree in order +// to expose this information? @" IReturnOperation (OperationKind.Return, Type: null) (Syntax: 'return " + (needCast ? "(int)" : "") + @"x;') ReturnedValue: @@ -24548,7 +24549,7 @@ .maxstack 1 Assert.Equal("return " + (needCast ? "(T)" : "") + @"x;", node.ToString()); VerifyOperationTreeForNode(compilation1, model, node, -// PROTOTYPE(StaticAbstractMembersInInterfaces): It feels like the "T" constraint is important for this operator, but it is not +// https://github.com/dotnet/roslyn/issues/53803: It feels like the "T" constraint is important for this operator, but it is not // reflected in the IOperation tree. Should we change the shape of the tree in order // to expose this information? @" From 87045f207ed9544913df70871986e0e62d4b488c Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Tue, 8 Jun 2021 10:02:34 -0700 Subject: [PATCH 090/127] Add enum value --- .../InheritanceMargin/InheritanceRelationship.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs b/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs index d5133fc5e8dd0..1bca4c260984d 100644 --- a/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs +++ b/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs @@ -45,7 +45,16 @@ internal enum InheritanceRelationship /// /// A compound value for indicating there are multiple targets both implementing the member and overriden by the member. /// - ImplementingOverridden = InheritanceRelationship.Implementing | InheritanceRelationship.Overridden - + ImplementingOverridden = InheritanceRelationship.Implementing | InheritanceRelationship.Overridden, + + ImplementedInterface, + BaseType, + DerivedType, + InheritedInterface, + ImplementedType, + ImplmentedMember, + OverriddenMember, + OverridingMember, + ImplementingMember } } From aa0fda644d6f99a5f0c2e146515a3314abbf7526 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Tue, 8 Jun 2021 11:24:32 -0700 Subject: [PATCH 091/127] Add new helpers in language service --- .../InheritanceMarginServiceHelpers.cs | 236 +++++++++++++++++- .../InheritanceRelationship.cs | 9 +- 2 files changed, 234 insertions(+), 11 deletions(-) diff --git a/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs b/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs index 02279bca1ab74..bbc2323ae646c 100644 --- a/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs +++ b/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs @@ -10,9 +10,12 @@ using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.FindSymbols.FindReferences; using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.InheritanceMargin { @@ -83,7 +86,7 @@ private static async ValueTask if (symbol is IEventSymbol or IPropertySymbol or IMethodSymbol) { - await AddInheritanceMemberItemsForTypeMembersAsync(solution, symbol, lineNumber, builder, cancellationToken).ConfigureAwait(false); + await AddInheritanceMemberItemsForClassAndStructMembersAsync(solution, symbol, lineNumber, builder, cancellationToken).ConfigureAwait(false); } } @@ -124,7 +127,7 @@ private static async ValueTask AddInheritanceMemberItemsForNamedTypeAsync( if (baseSymbols.Any() || derivedSymbols.Any()) { - var item = await CreateInheritanceMemberItemAsync( + var item = await CreateInheritanceMemberItemForNamedTypeAsync( solution, memberSymbol, lineNumber, @@ -135,7 +138,7 @@ private static async ValueTask AddInheritanceMemberItemsForNamedTypeAsync( } } - private static async ValueTask AddInheritanceMemberItemsForTypeMembersAsync( + private static async ValueTask AddInheritanceMemberItemsForClassAndStructMembersAsync( Solution solution, ISymbol memberSymbol, int lineNumber, @@ -183,7 +186,101 @@ private static async ValueTask AddInheritanceMemberItemsForTypeMembersAsync( } } - private static async ValueTask CreateInheritanceMemberItemAsync( + #region Interface + private static async ValueTask CreateInheritanceMemberItemForInterfaceAsync( + Solution solution, + INamedTypeSymbol interfaceSymbol, + int lineNumber, + ImmutableArray baseSymbols, + ImmutableArray derivedTypesSymbols, + CancellationToken cancellationToken) + { + var baseSymbolItems = await baseSymbols + .SelectAsArray(symbol => symbol.OriginalDefinition) + .Distinct() + .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( + solution, + symbol, + InheritanceRelationship.InheritedInterface, + cancellationToken), cancellationToken) + .ConfigureAwait(false); + + var derivedTypeItems = await derivedTypesSymbols + .SelectAsArray(symbol => symbol.OriginalDefinition) + .Distinct() + .SelectAsArrayAsync((symbol, _) => + CreateInheritanceItemAsync(solution, + symbol, + InheritanceRelationship.ImplementingType, + cancellationToken), cancellationToken) + .ConfigureAwait(false); + + return new SerializableInheritanceMarginItem( + lineNumber, + FindUsagesHelpers.GetDisplayParts(interfaceSymbol), + interfaceSymbol.GetGlyph(), + baseSymbolItems.Concat(derivedTypeItems)); + } + + private static async ValueTask CreateInheritanceMemberInfoForInterfaceMemberAsync( + Solution solution, + ISymbol memberSymbol, + int lineNumber, + ImmutableArray implementingMembers, + ImmutableArray implementedMembers, + ImmutableArray overridenMembers, + ImmutableArray overridingMembers, + CancellationToken cancellationToken) + { + var implementingMemberItems = await implementingMembers + .SelectAsArray(symbol => symbol.OriginalDefinition) + .Distinct() + .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( + solution, + symbol, + InheritanceRelationship.ImplmentedMember, + cancellationToken), cancellationToken).ConfigureAwait(false); + + var implementedMemberItems = await implementedMembers + .SelectAsArray(symbol => symbol.OriginalDefinition) + .Distinct() + .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( + solution, + symbol, + InheritanceRelationship.ImplementingMember, + cancellationToken), cancellationToken).ConfigureAwait(false); + + var overridenMemberItems = await overridenMembers + .SelectAsArray(symbol => symbol.OriginalDefinition) + .Distinct() + .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( + solution, + symbol, + InheritanceRelationship.OverriddenMember, + cancellationToken), cancellationToken).ConfigureAwait(false); + + var overridingMemberItems = await overridingMembers + .SelectAsArray(symbol => symbol.OriginalDefinition) + .Distinct() + .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( + solution, + symbol, + InheritanceRelationship.OverridingMember, + cancellationToken), cancellationToken).ConfigureAwait(false); + + return new SerializableInheritanceMarginItem( + lineNumber, + FindUsagesHelpers.GetDisplayParts(memberSymbol), + memberSymbol.GetGlyph(), + implementingMemberItems.Concat(implementedMemberItems) + .Concat(overridenMemberItems) + .Concat(overridingMemberItems)); + } + + #endregion + + #region ClassAndStruct + private static async ValueTask CreateInheritanceMemberItemForClassAndStructureAsync( Solution solution, INamedTypeSymbol memberSymbol, int lineNumber, @@ -194,13 +291,114 @@ private static async ValueTask CreateInherita var baseSymbolItems = await baseSymbols .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() - .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync(solution, symbol, InheritanceRelationship.Implementing, cancellationToken), cancellationToken) + .SelectAsArrayAsync((symbol, _) => + { + if (symbol.IsInterfaceType()) + { + return CreateInheritanceItemAsync( + solution, + symbol, + InheritanceRelationship.InheritedInterface, cancellationToken); + } + else + { + return CreateInheritanceItemAsync( + solution, + symbol, + InheritanceRelationship.BaseType, cancellationToken); + } + }, cancellationToken) .ConfigureAwait(false); var derivedTypeItems = await derivedTypesSymbols .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() - .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync(solution, symbol, InheritanceRelationship.Implemented, cancellationToken), cancellationToken) + .SelectAsArrayAsync((symbol, _) => + CreateInheritanceItemAsync(solution, + symbol, + InheritanceRelationship.DerivedType, + cancellationToken), cancellationToken) + .ConfigureAwait(false); + + return new SerializableInheritanceMarginItem( + lineNumber, + FindUsagesHelpers.GetDisplayParts(memberSymbol), + memberSymbol.GetGlyph(), + baseSymbolItems.Concat(derivedTypeItems)); + } + + private static async ValueTask CreateInheritanceMemberInfoForClassOrStructMemberAsync( + Solution solution, + ISymbol memberSymbol, + int lineNumber, + ImmutableArray implementingMembers, + ImmutableArray overridenMembers, + ImmutableArray overridingMembers, + CancellationToken cancellationToken) + { + var implementingMemberItems = await implementingMembers + .SelectAsArray(symbol => symbol.OriginalDefinition) + .Distinct() + .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( + solution, + symbol, + InheritanceRelationship.ImplmentedMember, + cancellationToken), cancellationToken).ConfigureAwait(false); + + var overridenMemberItems = await overridenMembers + .SelectAsArray(symbol => symbol.OriginalDefinition) + .Distinct() + .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( + solution, + symbol, + InheritanceRelationship.OverriddenMember, + cancellationToken), cancellationToken).ConfigureAwait(false); + + var overridingMemberItems = await overridingMembers + .SelectAsArray(symbol => symbol.OriginalDefinition) + .Distinct() + .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( + solution, + symbol, + InheritanceRelationship.OverridingMember, + cancellationToken), cancellationToken).ConfigureAwait(false); + + return new SerializableInheritanceMarginItem( + lineNumber, + FindUsagesHelpers.GetDisplayParts(memberSymbol), + memberSymbol.GetGlyph(), + implementingMemberItems + .Concat(overridenMemberItems) + .Concat(overridingMemberItems)); + } + #endregion + + private static async ValueTask CreateInheritanceMemberItemForNamedTypeAsync( + Solution solution, + INamedTypeSymbol memberSymbol, + int lineNumber, + ImmutableArray baseSymbols, + ImmutableArray derivedTypesSymbols, + CancellationToken cancellationToken) + { + var baseSymbolItems = await baseSymbols + .SelectAsArray(symbol => symbol.OriginalDefinition) + .Distinct() + .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( + solution, + symbol, + InheritanceRelationship.Implementing, + cancellationToken), cancellationToken) + .ConfigureAwait(false); + + var derivedTypeItems = await derivedTypesSymbols + .SelectAsArray(symbol => symbol.OriginalDefinition) + .Distinct() + .SelectAsArrayAsync((symbol, _) => + CreateInheritanceItemAsync(solution, + symbol, + InheritanceRelationship.Implemented, + cancellationToken), cancellationToken) .ConfigureAwait(false); return new SerializableInheritanceMarginItem( @@ -248,22 +446,38 @@ private static async ValueTask CreateInherita var implementingMemberItems = await implementingMembers .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() - .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync(solution, symbol, InheritanceRelationship.Implementing, cancellationToken), cancellationToken).ConfigureAwait(false); + .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( + solution, + symbol, + InheritanceRelationship.ImplmentedMember, + cancellationToken), cancellationToken).ConfigureAwait(false); var implementedMemberItems = await implementedMembers .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() - .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync(solution, symbol, InheritanceRelationship.Implemented, cancellationToken), cancellationToken).ConfigureAwait(false); + .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( + solution, + symbol, + InheritanceRelationship.Implemented, + cancellationToken), cancellationToken).ConfigureAwait(false); var overridenMemberItems = await overridenMembers .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() - .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync(solution, symbol, InheritanceRelationship.Overridden, cancellationToken), cancellationToken).ConfigureAwait(false); + .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( + solution, + symbol, + InheritanceRelationship.OverriddenMember, + cancellationToken), cancellationToken).ConfigureAwait(false); var overridingMemberItems = await overridingMembers .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() - .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync(solution, symbol, InheritanceRelationship.Overriding, cancellationToken), cancellationToken).ConfigureAwait(false); + .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( + solution, + symbol, + InheritanceRelationship.OverridingMember, + cancellationToken), cancellationToken).ConfigureAwait(false); return new SerializableInheritanceMarginItem( lineNumber, @@ -274,6 +488,7 @@ private static async ValueTask CreateInherita .Concat(overridingMemberItems)); } + #region FindAllReferenceHelpers private static ImmutableArray GetImplementingSymbolsForTypeMember( ISymbol memberSymbol, ImmutableArray overridingSymbols) @@ -402,5 +617,6 @@ private static async Task> GetDerivedTypesAndIm cancellationToken: cancellationToken).ConfigureAwait(false); } } + #endregion } } diff --git a/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs b/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs index 1bca4c260984d..8e65810316bb0 100644 --- a/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs +++ b/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs @@ -47,14 +47,21 @@ internal enum InheritanceRelationship /// ImplementingOverridden = InheritanceRelationship.Implementing | InheritanceRelationship.Overridden, + // class & struct ImplementedInterface, BaseType, DerivedType, + + // interface InheritedInterface, - ImplementedType, + ImplementingType, + + // class & structure members ImplmentedMember, OverriddenMember, OverridingMember, + + // member of interface ImplementingMember } } From 3dc7387d232ff2abdbfbc87758ceed209e054286 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Tue, 8 Jun 2021 11:51:58 -0700 Subject: [PATCH 092/127] Properly resolve method references in constructed generic types. (#53846) Also closes #53802. --- .../Metadata/PE/MemberRefMetadataDecoder.cs | 49 +--- .../Symbols/Metadata/PE/MetadataDecoder.cs | 11 +- .../RetargetingSymbolTranslator.cs | 69 ++++- ...berContainerSymbol_ImplementationChecks.cs | 2 +- .../CSharp/Portable/Symbols/TypeSymbol.cs | 2 - .../Semantics/InheritanceBindingTests.cs | 21 +- ...RetargetExplicitInterfaceImplementation.cs | 35 +++ .../StaticAbstractMembersInInterfacesTests.cs | 258 ++++++++++++++++++ .../Metadata/PE/MemberRefMetadataDecoder.vb | 44 +-- .../Symbols/Metadata/PE/MetadataDecoder.vb | 11 +- .../RetargetingSymbolTranslator.vb | 64 ++++- .../Metadata/PE/LoadingMethods.vb | 119 ++++++++ .../Retargeting/RetargetingTests.vb | 34 +++ 13 files changed, 617 insertions(+), 102 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs index c7e557d1d6f55..027115bf4bc27 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs @@ -25,7 +25,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE internal sealed class MemberRefMetadataDecoder : MetadataDecoder { /// - /// Type context for resolving generic type arguments. + /// Type context for resolving generic type parameters. /// private readonly TypeSymbol _containingType; @@ -52,40 +52,25 @@ protected override TypeSymbol GetGenericMethodTypeParamSymbol(int position) } /// - /// This override changes two things: - /// 1) Return type arguments instead of type parameters. - /// 2) Handle non-PE types. + /// This override can handle non-PE types. /// protected override TypeSymbol GetGenericTypeParamSymbol(int position) { PENamedTypeSymbol peType = _containingType as PENamedTypeSymbol; if ((object)peType != null) { - while ((object)peType != null && (peType.MetadataArity - peType.Arity) > position) - { - peType = peType.ContainingSymbol as PENamedTypeSymbol; - } - - if ((object)peType == null || peType.MetadataArity <= position) - { - return new UnsupportedMetadataTypeSymbol(); // position of type parameter too large - } - - position -= peType.MetadataArity - peType.Arity; - Debug.Assert(position >= 0 && position < peType.Arity); - - return peType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[position].Type; //NB: args, not params + return base.GetGenericTypeParamSymbol(position); } NamedTypeSymbol namedType = _containingType as NamedTypeSymbol; if ((object)namedType != null) { int cumulativeArity; - TypeSymbol typeArgument; - GetGenericTypeArgumentSymbol(position, namedType, out cumulativeArity, out typeArgument); - if ((object)typeArgument != null) + TypeParameterSymbol typeParameter; + GetGenericTypeParameterSymbol(position, namedType, out cumulativeArity, out typeParameter); + if ((object)typeParameter != null) { - return typeArgument; + return typeParameter; } else { @@ -97,7 +82,7 @@ protected override TypeSymbol GetGenericTypeParamSymbol(int position) return new UnsupportedMetadataTypeSymbol(); // associated type does not have type parameters } - private static void GetGenericTypeArgumentSymbol(int position, NamedTypeSymbol namedType, out int cumulativeArity, out TypeSymbol typeArgument) + private static void GetGenericTypeParameterSymbol(int position, NamedTypeSymbol namedType, out int cumulativeArity, out TypeParameterSymbol typeArgument) { cumulativeArity = namedType.Arity; typeArgument = null; @@ -108,7 +93,7 @@ private static void GetGenericTypeArgumentSymbol(int position, NamedTypeSymbol n if ((object)containingType != null) { int containingTypeCumulativeArity; - GetGenericTypeArgumentSymbol(position, containingType, out containingTypeCumulativeArity, out typeArgument); + GetGenericTypeParameterSymbol(position, containingType, out containingTypeCumulativeArity, out typeArgument); cumulativeArity += containingTypeCumulativeArity; arityOffset = containingTypeCumulativeArity; } @@ -117,25 +102,19 @@ private static void GetGenericTypeArgumentSymbol(int position, NamedTypeSymbol n { Debug.Assert((object)typeArgument == null); - typeArgument = namedType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[position - arityOffset].Type; + typeArgument = namedType.TypeParameters[position - arityOffset]; } } /// - /// Search through the members of a given type symbol to find the method that matches a particular + /// Search through the members of the type symbol to find the method that matches a particular /// signature. /// - /// Type containing the desired method symbol. /// A MemberRef handle that can be used to obtain the name and signature of the method /// True to only return a method. /// The matching method symbol, or null if the inputs do not correspond to a valid method. - internal Symbol FindMember(TypeSymbol targetTypeSymbol, MemberReferenceHandle memberRef, bool methodsOnly) + internal Symbol FindMember(MemberReferenceHandle memberRef, bool methodsOnly) { - if ((object)targetTypeSymbol == null) - { - return null; - } - try { string memberName = Module.GetMemberRefNameOrThrow(memberRef); @@ -150,7 +129,7 @@ internal Symbol FindMember(TypeSymbol targetTypeSymbol, MemberReferenceHandle me case (byte)SignatureCallingConvention.VarArgs: int typeParamCount; ParamInfo[] targetParamInfo = this.DecodeSignatureParametersOrThrow(ref signaturePointer, signatureHeader, out typeParamCount); - return FindMethodBySignature(targetTypeSymbol, memberName, signatureHeader, typeParamCount, targetParamInfo); + return FindMethodBySignature(_containingType, memberName, signatureHeader, typeParamCount, targetParamInfo); case (byte)SignatureKind.Field: if (methodsOnly) @@ -161,7 +140,7 @@ internal Symbol FindMember(TypeSymbol targetTypeSymbol, MemberReferenceHandle me ImmutableArray> customModifiers; TypeSymbol type = this.DecodeFieldSignature(ref signaturePointer, out customModifiers); - return FindFieldBySignature(targetTypeSymbol, memberName, customModifiers, type); + return FindFieldBySignature(_containingType, memberName, customModifiers, type); default: // error: unexpected calling convention diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MetadataDecoder.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MetadataDecoder.cs index 0cb4d8bc019c6..1e11140687379 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MetadataDecoder.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MetadataDecoder.cs @@ -535,9 +535,16 @@ internal override Symbol GetSymbolForMemberRef(MemberReferenceHandle memberRef, // We're going to use a special decoder that can generate usable symbols for type parameters without full context. // (We're not just using a different type - we're also changing the type context.) - var memberRefDecoder = new MemberRefMetadataDecoder(moduleSymbol, targetTypeSymbol); + var memberRefDecoder = new MemberRefMetadataDecoder(moduleSymbol, targetTypeSymbol.OriginalDefinition); - return memberRefDecoder.FindMember(targetTypeSymbol, memberRef, methodsOnly); + var definition = memberRefDecoder.FindMember(memberRef, methodsOnly); + + if (definition is not null && !targetTypeSymbol.IsDefinition) + { + return definition.SymbolAsMember((NamedTypeSymbol)targetTypeSymbol); + } + + return definition; } protected override void EnqueueTypeSymbolInterfacesAndBaseTypes(Queue typeDefsToSearch, Queue typeSymbolsToSearch, TypeSymbol typeSymbol) diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs index b768d73d75286..be8ece2a7ba76 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs @@ -895,6 +895,8 @@ public MethodSymbol Retarget(MethodSymbol method) public MethodSymbol Retarget(MethodSymbol method, IEqualityComparer retargetedMethodComparer) { + Debug.Assert((object)method == method.ConstructedFrom); + if (ReferenceEquals(method.ContainingModule, this.UnderlyingModule) && ReferenceEquals(method, method.OriginalDefinition)) { return Retarget(method); @@ -903,10 +905,29 @@ public MethodSymbol Retarget(MethodSymbol method, IEqualityComparer retargetedMethodComparer) { - if (!method.IsGenericMethod) + if (!method.IsGenericMethod && !retargetedType.IsGenericType) { return FindWorker(translator, method, retargetedType, retargetedMethodComparer); } - // A generic method needs special handling because its signature is very likely - // to refer to method's type parameters. - var finder = new RetargetedTypeMethodFinder(translator._retargetingModule); + // A generic method or a method in generic type needs special handling because its signature is very likely + // to refer to method's or type's type parameters. + var finder = new RetargetedTypeMethodFinder(translator._retargetingModule, retargetedType, method); return FindWorker(finder, method, retargetedType, retargetedMethodComparer); } @@ -1035,16 +1061,31 @@ IEqualityComparer retargetedMethodComparer public override TypeParameterSymbol Retarget(TypeParameterSymbol typeParameter) { - if (ReferenceEquals(typeParameter.ContainingModule, this.UnderlyingModule)) + if (typeParameter.TypeParameterKind == TypeParameterKind.Method) { - return base.Retarget(typeParameter); + Debug.Assert((object)typeParameter.ContainingSymbol == _toFind); + + // The method symbol we are building will be using IndexedTypeParameterSymbols as + // its type parameters, therefore, we should return them here as well. + return IndexedTypeParameterSymbol.GetTypeParameter(typeParameter.Ordinal); } - Debug.Assert(typeParameter.TypeParameterKind == TypeParameterKind.Method); + NamedTypeSymbol containingType = _toFind.ContainingType; + NamedTypeSymbol retargetedContainingType = _retargetedType; + + do + { + if ((object)containingType == typeParameter.ContainingSymbol) + { + return retargetedContainingType.TypeParameters[typeParameter.Ordinal]; + } + + containingType = containingType.ContainingType; + retargetedContainingType = retargetedContainingType.ContainingType; + } + while (containingType is object); - // The method symbol we are building will be using IndexedTypeParameterSymbols as - // its type parameters, therefore, we should return them here as well. - return IndexedTypeParameterSymbol.GetTypeParameter(typeParameter.Ordinal); + throw ExceptionUtilities.Unreachable; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs index 8ca2918337dc6..afed53bee1e6a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs @@ -131,7 +131,7 @@ private SynthesizedExplicitImplementations ComputeInterfaceImplementations( HasBaseTypeDeclaringInterfaceResult? hasBaseClassDeclaringInterface = null; - foreach (var interfaceMember in @interface.GetMembersUnordered()) + foreach (var interfaceMember in @interface.GetMembers()) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs index 96b437d9c5425..c1b7025675bf4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs @@ -1690,8 +1690,6 @@ private static void ReportImplicitImplementationMatchDiagnostics(Symbol interfac if (member.DeclaredAccessibility != Accessibility.Public || member.IsStatic || member == implicitImpl) { //do nothing - not an ambiguous implementation - - // https://github.com/dotnet/roslyn/issues/53802: We likely need to do something here for static members. } else if (MemberSignatureComparer.RuntimeImplicitImplementationComparer.Equals(interfaceMember, member) && !member.IsAccessor()) { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs index 27985e7f8273e..475be24bb3db1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs @@ -4793,6 +4793,16 @@ public void Method(T i) { } public class Derived : Base, Interface { } + +class Other : Interface +{ + void Interface.Method(int i) { } +} + +class YetAnother : Interface +{ + public void Method(int i) { } +} "; //Both Base methods implement Interface.Method(int) //Both Base methods implement Interface.Method(T) @@ -4806,7 +4816,16 @@ public class Derived : Base, Interface Diagnostic(ErrorCode.WRN_MultipleRuntimeImplementationMatches, "Interface").WithArguments("Base.Method(int)", "Interface.Method(int)", "Derived").WithLocation(15, 35), // (15,35): warning CS1956: Member 'Base.Method(int)' implements interface member 'Interface.Method(int)' in type 'Derived'. There are multiple matches for the interface member at run-time. It is implementation dependent which method will be called. // public class Derived : Base, Interface - Diagnostic(ErrorCode.WRN_MultipleRuntimeImplementationMatches, "Interface").WithArguments("Base.Method(int)", "Interface.Method(int)", "Derived").WithLocation(15, 35) + Diagnostic(ErrorCode.WRN_MultipleRuntimeImplementationMatches, "Interface").WithArguments("Base.Method(int)", "Interface.Method(int)", "Derived").WithLocation(15, 35), + // (19,15): error CS0535: 'Other' does not implement interface member 'Interface.Method(int)' + // class Other : Interface + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "Interface").WithArguments("Other", "Interface.Method(int)").WithLocation(19, 15), + // (19,15): error CS0535: 'Other' does not implement interface member 'Interface.Method(int)' + // class Other : Interface + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "Interface").WithArguments("Other", "Interface.Method(int)").WithLocation(19, 15), + // (21,30): warning CS0473: Explicit interface implementation 'Other.Interface.Method(int)' matches more than one interface member. Which interface member is actually chosen is implementation-dependent. Consider using a non-explicit implementation instead. + // void Interface.Method(int i) { } + Diagnostic(ErrorCode.WRN_ExplicitImplCollision, "Method").WithArguments("Other.Interface.Method(int)").WithLocation(21, 30) ); } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Retargeting/RetargetExplicitInterfaceImplementation.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Retargeting/RetargetExplicitInterfaceImplementation.cs index dd9c78b23f953..3258c61bea7a9 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Retargeting/RetargetExplicitInterfaceImplementation.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Retargeting/RetargetExplicitInterfaceImplementation.cs @@ -436,5 +436,40 @@ public class D3 : C3 var retargetedClassC3Event1Impl = retargetedClassC3Event1.ExplicitInterfaceImplementations.Single(); Assert.Same(interfaceV2Event1, retargetedClassC3Event1Impl.OriginalDefinition); } + + [Fact] + public void ExplicitInterfaceImplementationRetargetingGenericType() + { + var source1 = @" +public class C1 +{ + public interface I1 + { + void M(T x); + } +} +"; + var ref1 = CreateEmptyCompilation("").ToMetadataReference(); + var compilation1 = CreateCompilation(source1, references: new[] { ref1 }); + + var source2 = @" +public class C2 : C1.I1 +{ + void C1.I1.M(U x) {} +} +"; + var compilation2 = CreateCompilation(source2, references: new[] { compilation1.ToMetadataReference(), ref1, CreateEmptyCompilation("").ToMetadataReference() }); + + var compilation3 = CreateCompilation("", references: new[] { compilation1.ToMetadataReference(), compilation2.ToMetadataReference() }); + + Assert.NotSame(compilation2.GetTypeByMetadataName("C1`1"), compilation3.GetTypeByMetadataName("C1`1")); + + var c2 = compilation3.GetTypeByMetadataName("C2`1"); + Assert.IsType(c2); + + var m = c2.GetMethod("C1.I1.M"); + + Assert.Equal(c2.Interfaces().Single().GetMethod("M"), m.ExplicitInterfaceImplementations.Single()); + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index 032925be05ba8..6539084721f1d 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -25088,5 +25088,263 @@ static T M03(int y) where T : U where U : I1 Diagnostic((op == "explicit" ? ErrorCode.ERR_NoExplicitConv : ErrorCode.ERR_NoImplicitConv), (needCast ? "(T)" : "") + "x").WithArguments("int", "T").WithLocation(15, 16) ); } + + [Fact] + [WorkItem(53802, "https://github.com/dotnet/roslyn/issues/53802")] + public void TestAmbiguousImplementationMethod_01() + { + var source1 = @" +public interface Interface +{ + abstract static void Method(int i); + abstract static void Method(T i); + abstract static void Method(U i); +} + +public class Base : Interface +{ + public static void Method(int i) { } + public static void Method(T i) { } +} + +public class Derived : Base, Interface +{ +} + +class YetAnother : Interface +{ + public static void Method(int i) { } +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var b = module.GlobalNamespace.GetTypeMember("Base"); + var bI = b.Interfaces().Single(); + var biMethods = bI.GetMembers(); + + Assert.Equal("void Interface.Method(System.Int32 i)", biMethods[0].OriginalDefinition.ToTestDisplayString()); + Assert.Equal("void Interface.Method(T i)", biMethods[1].OriginalDefinition.ToTestDisplayString()); + Assert.Equal("void Interface.Method(U i)", biMethods[2].OriginalDefinition.ToTestDisplayString()); + + var bM1 = b.FindImplementationForInterfaceMember(biMethods[0]); + + Assert.Equal("void Base.Method(System.Int32 i)", bM1.ToTestDisplayString()); + + var bM2 = b.FindImplementationForInterfaceMember(biMethods[1]); + + Assert.Equal("void Base.Method(T i)", bM2.ToTestDisplayString()); + Assert.Same(bM2, b.FindImplementationForInterfaceMember(biMethods[2])); + + var bM1Impl = ((MethodSymbol)bM1).ExplicitInterfaceImplementations; + var bM2Impl = ((MethodSymbol)bM2).ExplicitInterfaceImplementations; + + if (module is PEModuleSymbol) + { + Assert.Equal(biMethods[0], bM1Impl.Single()); + + Assert.Equal(2, bM2Impl.Length); + Assert.Equal(biMethods[1], bM2Impl[0]); + Assert.Equal(biMethods[2], bM2Impl[1]); + } + else + { + Assert.Empty(bM1Impl); + Assert.Empty(bM2Impl); + } + + var d = module.GlobalNamespace.GetTypeMember("Derived"); + var dB = d.BaseTypeNoUseSiteDiagnostics; + var dI = d.Interfaces().Single(); + var diMethods = dI.GetMembers(); + + Assert.Equal("void Interface.Method(System.Int32 i)", diMethods[0].OriginalDefinition.ToTestDisplayString()); + Assert.Equal("void Interface.Method(T i)", diMethods[1].OriginalDefinition.ToTestDisplayString()); + Assert.Equal("void Interface.Method(U i)", diMethods[2].OriginalDefinition.ToTestDisplayString()); + + var dM1 = d.FindImplementationForInterfaceMember(diMethods[0]); + + Assert.Same(bM1, dM1.OriginalDefinition); + + var dM2 = d.FindImplementationForInterfaceMember(diMethods[1]); + + Assert.Same(bM2, dM2.OriginalDefinition); + Assert.Same(bM2, d.FindImplementationForInterfaceMember(diMethods[2]).OriginalDefinition); + } + } + + [Fact] + [WorkItem(53802, "https://github.com/dotnet/roslyn/issues/53802")] + public void TestAmbiguousImplementationMethod_02() + { + var source0 = @" +public interface Interface +{ + abstract static void Method(int i); + abstract static void Method(T i); + abstract static void Method(U i); +} + +public class Base : Interface +{ + public static void Method(int i) { } + public static void Method(T i) { } +} +"; + + var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation0.VerifyDiagnostics(); + + var source1 = @" +public class Derived : Base, Interface +{ +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation0.EmitToImageReference() }); + + CompileAndVerify(compilation1, sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.Skipped).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var d = module.GlobalNamespace.GetTypeMember("Derived"); + var dB = d.BaseTypeNoUseSiteDiagnostics; + var dI = d.Interfaces().Single(); + var diMethods = dI.GetMembers(); + + Assert.Equal("void Interface.Method(System.Int32 i)", diMethods[0].OriginalDefinition.ToTestDisplayString()); + Assert.Equal("void Interface.Method(T i)", diMethods[1].OriginalDefinition.ToTestDisplayString()); + Assert.Equal("void Interface.Method(U i)", diMethods[2].OriginalDefinition.ToTestDisplayString()); + + var dM1 = d.FindImplementationForInterfaceMember(diMethods[0]); + + Assert.Equal("void Base.Method(System.Int32 i)", dM1.OriginalDefinition.ToTestDisplayString()); + + var dM2 = d.FindImplementationForInterfaceMember(diMethods[1]); + + Assert.Equal("void Base.Method(T i)", dM2.OriginalDefinition.ToTestDisplayString()); + Assert.Same(dM2, d.FindImplementationForInterfaceMember(diMethods[2])); + + var dM1Impl = ((MethodSymbol)dM1).ExplicitInterfaceImplementations; + var dM2Impl = ((MethodSymbol)dM2).ExplicitInterfaceImplementations; + + Assert.Equal(diMethods[0], dM1Impl.Single()); + + Assert.Equal(2, dM2Impl.Length); + Assert.Equal(diMethods[1], dM2Impl[0]); + Assert.Equal(diMethods[2], dM2Impl[1]); + } + } + + [Fact] + [WorkItem(53802, "https://github.com/dotnet/roslyn/issues/53802")] + public void TestAmbiguousImplementationMethod_03() + { + var source0 = @" +public interface Interface +{ + abstract static void Method(int i); + abstract static void Method(T i); + abstract static void Method(U i); +} + +public class Base : Interface +{ + public static void Method(int i) { } + public static void Method(T i) { } +} +"; + + var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { CreateEmptyCompilation("").ToMetadataReference() }); + + compilation0.VerifyDiagnostics(); + + var source1 = @" +public class Derived : Base, Interface +{ +} +"; + + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation0.ToMetadataReference() }); + + var d = compilation1.GlobalNamespace.GetTypeMember("Derived"); + var dB = d.BaseTypeNoUseSiteDiagnostics; + var dI = d.Interfaces().Single(); + var diMethods = dI.GetMembers(); + + Assert.IsType(dB.OriginalDefinition); + + Assert.Equal("void Interface.Method(System.Int32 i)", diMethods[0].OriginalDefinition.ToTestDisplayString()); + Assert.Equal("void Interface.Method(T i)", diMethods[1].OriginalDefinition.ToTestDisplayString()); + Assert.Equal("void Interface.Method(U i)", diMethods[2].OriginalDefinition.ToTestDisplayString()); + + var dM1 = d.FindImplementationForInterfaceMember(diMethods[0]); + + Assert.Equal("void Base.Method(System.Int32 i)", dM1.OriginalDefinition.ToTestDisplayString()); + + var dM2 = d.FindImplementationForInterfaceMember(diMethods[1]); + + Assert.Equal("void Base.Method(T i)", dM2.OriginalDefinition.ToTestDisplayString()); + Assert.Equal(dM2, d.FindImplementationForInterfaceMember(diMethods[2])); + + var dM1Impl = ((MethodSymbol)dM1).ExplicitInterfaceImplementations; + var dM2Impl = ((MethodSymbol)dM2).ExplicitInterfaceImplementations; + + Assert.Empty(dM1Impl); + Assert.Empty(dM2Impl); + } + + [Fact] + [WorkItem(53802, "https://github.com/dotnet/roslyn/issues/53802")] + public void TestAmbiguousImplementationMethod_04() + { + var source2 = @" +public interface Interface +{ + abstract static void Method(int i); + abstract static void Method(T i); + abstract static void Method(U i); +} + +class Other : Interface +{ + static void Interface.Method(int i) { } +} +"; + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation2.VerifyDiagnostics( + // (9,15): error CS0535: 'Other' does not implement interface member 'Interface.Method(int)' + // class Other : Interface + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "Interface").WithArguments("Other", "Interface.Method(int)").WithLocation(9, 15), + // (9,15): error CS0535: 'Other' does not implement interface member 'Interface.Method(int)' + // class Other : Interface + Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "Interface").WithArguments("Other", "Interface.Method(int)").WithLocation(9, 15), + // (11,37): warning CS0473: Explicit interface implementation 'Other.Interface.Method(int)' matches more than one interface member. Which interface member is actually chosen is implementation-dependent. Consider using a non-explicit implementation instead. + // static void Interface.Method(int i) { } + Diagnostic(ErrorCode.WRN_ExplicitImplCollision, "Method").WithArguments("Other.Interface.Method(int)").WithLocation(11, 37) + ); + } } } diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.vb index a1dee50b3df56..3e3985ba83fea 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.vb @@ -43,35 +43,22 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE End Function ''' - ''' This override changes two things: - ''' 1) Return type arguments instead of type parameters. - ''' 2) Handle non-PE types. + ''' This override can handle non-PE types. ''' Protected Overrides Function GetGenericTypeParamSymbol(position As Integer) As TypeSymbol Dim peType As PENamedTypeSymbol = TryCast(Me._containingType, PENamedTypeSymbol) If peType IsNot Nothing Then - While peType IsNot Nothing AndAlso (peType.MetadataArity - peType.Arity) > position - peType = TryCast(peType.ContainingSymbol, PENamedTypeSymbol) - End While - - If peType Is Nothing OrElse peType.MetadataArity <= position Then - Return New UnsupportedMetadataTypeSymbol(VBResources.PositionOfTypeParameterTooLarge) - End If - - position -= peType.MetadataArity - peType.Arity - Debug.Assert(position >= 0 AndAlso position < peType.Arity) - - Return peType.TypeArgumentsNoUseSiteDiagnostics(position) + Return MyBase.GetGenericTypeParamSymbol(position) End If Dim namedType As NamedTypeSymbol = TryCast(Me._containingType, NamedTypeSymbol) If namedType IsNot Nothing Then Dim cumulativeArity As Integer - Dim typeArgument As TypeSymbol = Nothing + Dim typeParameter As TypeParameterSymbol = Nothing - GetGenericTypeArgumentSymbol(position, namedType, cumulativeArity, typeArgument) - If typeArgument IsNot Nothing Then - Return typeArgument + GetGenericTypeParameterSymbol(position, namedType, cumulativeArity, typeParameter) + If typeParameter IsNot Nothing Then + Return typeParameter Else Debug.Assert(cumulativeArity <= position) Return New UnsupportedMetadataTypeSymbol(VBResources.PositionOfTypeParameterTooLarge) @@ -81,7 +68,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Return New UnsupportedMetadataTypeSymbol(VBResources.AssociatedTypeDoesNotHaveTypeParameters) End Function - Private Shared Sub GetGenericTypeArgumentSymbol(position As Integer, namedType As NamedTypeSymbol, ByRef cumulativeArity As Integer, ByRef typeArgument As TypeSymbol) + Private Shared Sub GetGenericTypeParameterSymbol(position As Integer, namedType As NamedTypeSymbol, ByRef cumulativeArity As Integer, ByRef typeArgument As TypeParameterSymbol) cumulativeArity = namedType.Arity typeArgument = Nothing @@ -91,29 +78,24 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE If containingType IsNot Nothing Then Dim containingTypeCumulativeArity As Integer - GetGenericTypeArgumentSymbol(position, containingType, containingTypeCumulativeArity, typeArgument) + GetGenericTypeParameterSymbol(position, containingType, containingTypeCumulativeArity, typeArgument) cumulativeArity += containingTypeCumulativeArity arityOffset = containingTypeCumulativeArity End If If arityOffset <= position AndAlso position < cumulativeArity Then Debug.Assert(typeArgument Is Nothing) - typeArgument = namedType.TypeArgumentsNoUseSiteDiagnostics(position - arityOffset) + typeArgument = namedType.TypeParameters(position - arityOffset) End If End Sub ''' - ''' Search through the members of a given type symbol to find the method that matches a particular signature. + ''' Search through the members of the type symbol to find the method that matches a particular signature. ''' - ''' Type containing the desired method symbol. ''' A MemberRef handle that can be used to obtain the name and signature of the method ''' True to only return a method. ''' The matching method symbol, or null if the inputs do not correspond to a valid method. - Friend Function FindMember(targetTypeSymbol As TypeSymbol, memberRef As MemberReferenceHandle, methodsOnly As Boolean) As Symbol - If targetTypeSymbol Is Nothing Then - Return Nothing - End If - + Friend Function FindMember(memberRef As MemberReferenceHandle, methodsOnly As Boolean) As Symbol Try Dim memberName As String = [Module].GetMemberRefNameOrThrow(memberRef) Dim signatureHandle = [Module].GetSignatureOrThrow(memberRef) @@ -124,7 +106,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Case SignatureCallingConvention.Default, SignatureCallingConvention.VarArgs Dim typeParamCount As Integer Dim targetParamInfo As ParamInfo(Of TypeSymbol)() = Me.DecodeSignatureParametersOrThrow(signaturePointer, signatureHeader, typeParamCount) - Return FindMethodBySignature(targetTypeSymbol, memberName, signatureHeader, typeParamCount, targetParamInfo) + Return FindMethodBySignature(_containingType, memberName, signatureHeader, typeParamCount, targetParamInfo) Case SignatureKind.Field If methodsOnly Then @@ -134,7 +116,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Dim customModifiers As ImmutableArray(Of ModifierInfo(Of TypeSymbol)) = Nothing Dim type As TypeSymbol = Me.DecodeFieldSignature(signaturePointer, customModifiers) - Return FindFieldBySignature(targetTypeSymbol, memberName, customModifiers, type) + Return FindFieldBySignature(_containingType, memberName, customModifiers, type) Case Else ' error diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MetadataDecoder.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MetadataDecoder.vb index 012387b04a03d..b5c1a53d2eada 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MetadataDecoder.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MetadataDecoder.vb @@ -458,8 +458,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE ' We're going to use a special decoder that can generate usable symbols for type parameters without full context. ' (We're not just using a different type - we're also changing the type context.) - Dim memberRefDecoder = New MemberRefMetadataDecoder(moduleSymbol, targetTypeSymbol) - Return memberRefDecoder.FindMember(targetTypeSymbol, memberRef, methodsOnly) + Dim memberRefDecoder = New MemberRefMetadataDecoder(moduleSymbol, targetTypeSymbol.OriginalDefinition) + + Dim definition = memberRefDecoder.FindMember(memberRef, methodsOnly) + + If definition IsNot Nothing AndAlso Not targetTypeSymbol.IsDefinition Then + Return definition.AsMember(DirectCast(targetTypeSymbol, NamedTypeSymbol)) + End If + + Return definition End Function Protected Overrides Sub EnqueueTypeSymbolInterfacesAndBaseTypes(typeDefsToSearch As Queue(Of TypeDefinitionHandle), typeSymbolsToSearch As Queue(Of TypeSymbol), typeSymbol As TypeSymbol) diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.vb b/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.vb index 59ce74a4b4a2c..46e7c7e6a1bb2 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.vb @@ -884,6 +884,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Retargeting End Function Public Function Retarget(method As MethodSymbol, retargetedMethodComparer As IEqualityComparer(Of MethodSymbol)) As MethodSymbol + Debug.Assert(method Is method.ConstructedFrom) + If method.ContainingModule Is Me.UnderlyingModule AndAlso method.IsDefinition Then Return DirectCast(SymbolMap.GetOrAdd(method, _retargetingModule._createRetargetingMethod), RetargetingMethodSymbol) End If @@ -891,10 +893,26 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Retargeting Dim containingType = method.ContainingType Dim retargetedType = Retarget(containingType, RetargetOptions.RetargetPrimitiveTypesByName) + If retargetedType Is containingType Then + Return method + End If + + If Not containingType.IsDefinition Then + Debug.Assert(Not retargetedType.IsDefinition) + + Dim retargetedDefinition = Retarget(method.OriginalDefinition, retargetedMethodComparer) + + If retargetedDefinition Is Nothing Then + Return Nothing + End If + + Return retargetedDefinition.AsMember(retargetedType) + End If + + Debug.Assert(retargetedType.IsDefinition) + ' NB: may return null if the method cannot be found in the retargeted type (e.g. removed in a subsequent version) - Return If(retargetedType Is containingType, - method, - FindMethodInRetargetedType(method, retargetedType, retargetedMethodComparer)) + Return FindMethodInRetargetedType(method, retargetedType, retargetedMethodComparer) End Function Private Function FindMethodInRetargetedType(method As MethodSymbol, retargetedType As NamedTypeSymbol, retargetedMethodComparer As IEqualityComparer(Of MethodSymbol)) As MethodSymbol @@ -904,8 +922,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Retargeting Private Class RetargetedTypeMethodFinder Inherits RetargetingSymbolTranslator - Private Sub New(retargetingModule As RetargetingModuleSymbol) + Private ReadOnly _retargetedType As NamedTypeSymbol + Private ReadOnly _toFind As MethodSymbol + + Private Sub New(retargetingModule As RetargetingModuleSymbol, retargetedType As NamedTypeSymbol, toFind As MethodSymbol) MyBase.New(retargetingModule) + + _retargetedType = retargetedType + _toFind = toFind End Sub Public Shared Function Find( @@ -918,7 +942,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Retargeting Return Nothing End If - If Not method.IsGenericMethod Then + If Not method.IsGenericMethod AndAlso Not retargetedType.IsGenericType Then Return FindWorker(translator, method, retargetedType, retargetedMethodComparer) End If @@ -926,9 +950,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Retargeting ' among members of a type, constructed methods are never returned through GetMembers API. Debug.Assert(method Is method.ConstructedFrom) - ' A generic method needs special handling because its signature is very likely - ' to refer to method's type parameters. - Dim finder = New RetargetedTypeMethodFinder(translator._retargetingModule) + ' A generic method or a method in generic type needs special handling because its signature is very likely + ' to refer to method's or type's type parameters. + Dim finder = New RetargetedTypeMethodFinder(translator._retargetingModule, retargetedType, method) Return FindWorker(finder, method, retargetedType, retargetedMethodComparer) End Function @@ -977,15 +1001,27 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Retargeting End Function Public Overrides Function Retarget(typeParameter As TypeParameterSymbol) As TypeParameterSymbol - If typeParameter.ContainingModule Is Me.UnderlyingModule Then - Return MyBase.Retarget(typeParameter) + If typeParameter.TypeParameterKind = TypeParameterKind.Method Then + Debug.Assert(typeParameter.ContainingSymbol Is _toFind) + + ' The method symbol we are building will be using IndexedTypeParameterSymbols as + ' its type parameters, therefore, we should return them here as well. + Return IndexedTypeParameterSymbol.GetTypeParameter(typeParameter.Ordinal) End If - Debug.Assert(typeParameter.TypeParameterKind = TypeParameterKind.Method) + Dim containingType As NamedTypeSymbol = _toFind.ContainingType + Dim retargetedContainingType As NamedTypeSymbol = _retargetedType + + Do + If containingType Is typeParameter.ContainingSymbol Then + Return retargetedContainingType.TypeParameters(typeParameter.Ordinal) + End If + + containingType = containingType.ContainingType + retargetedContainingType = retargetedContainingType.ContainingType + Loop While containingType IsNot Nothing - ' The method symbol we are building will be using IndexedTypeParameterSymbols as - ' its type parameters, therefore, we should return them here as well. - Return IndexedTypeParameterSymbol.GetTypeParameter(typeParameter.Ordinal) + Throw ExceptionUtilities.Unreachable End Function End Class diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/LoadingMethods.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/LoadingMethods.vb index 3003d9d737eb9..87e3287ef704a 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/LoadingMethods.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/LoadingMethods.vb @@ -965,5 +965,124 @@ BC30390: 'C2.Private Overloads Sub M2()' is not accessible in this context becau ]]>) End Sub + + + Public Sub TestAmbiguousImplementationMethod() + + Dim ilSource = + +{ + // Methods + .method public hidebysig abstract virtual + void Method ( + int32 i + ) cil managed + { + } // end of method Interface`2::Method + + .method public hidebysig abstract virtual + void Method ( + !T i + ) cil managed + { + } // end of method Interface`2::Method + + .method public hidebysig abstract virtual + void Method ( + !U i + ) cil managed + { + } // end of method Interface`2::Method + +} // end of class Interface`2 + +.class public auto ansi beforefieldinit Base`1 + extends [mscorlib]System.Object + implements class Interface`2 +{ + // Methods + .method public hidebysig newslot virtual + void Method ( + int32 i + ) cil managed + { + .override method instance void class Interface`2::Method(int32) + // Method begins at RVA 0x2050 + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: nop + IL_0001: ret + } // end of method Base`1::Method + + .method public hidebysig newslot virtual + void Method ( + !T i + ) cil managed + { + .override method instance void class Interface`2::Method(!0) + .override method instance void class Interface`2::Method(!1) + // Method begins at RVA 0x2050 + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: nop + IL_0001: ret + } // end of method Base`1::Method + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2053 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method Base`1::.ctor + +} // end of class Base`1 +]]> + + Dim compilationDef = + + +Option Strict Off + +Imports System + +Module Module1 + Sub Main() + End Sub +End Module + + + + Dim compilation = CompilationUtils.CreateCompilationWithCustomILSource(compilationDef, ilSource.Value, includeVbRuntime:=True, options:=TestOptions.ReleaseExe) + + Dim b = compilation.GlobalNamespace.GetTypeMember("Base") + Dim bI = b.Interfaces().Single() + Dim biMethods = bI.GetMembers() + + Assert.Equal("Sub [Interface](Of T, U).Method(i As System.Int32)", biMethods(0).OriginalDefinition.ToTestDisplayString()) + Assert.Equal("Sub [Interface](Of T, U).Method(i As T)", biMethods(1).OriginalDefinition.ToTestDisplayString()) + Assert.Equal("Sub [Interface](Of T, U).Method(i As U)", biMethods(2).OriginalDefinition.ToTestDisplayString()) + + Dim bMethods = b.GetMembers() + + Assert.Equal("Sub Base(Of T).Method(i As System.Int32)", bMethods(0).ToTestDisplayString()) + Assert.Equal("Sub Base(Of T).Method(i As T)", bMethods(1).ToTestDisplayString()) + + Dim bM1Impl = DirectCast(bMethods(0), MethodSymbol).ExplicitInterfaceImplementations + Dim bM2Impl = DirectCast(bMethods(1), MethodSymbol).ExplicitInterfaceImplementations + Assert.Equal(biMethods(0), bM1Impl.Single()) + + Assert.Equal(2, bM2Impl.Length) + Assert.Equal(biMethods(1), bM2Impl(0)) + Assert.Equal(biMethods(2), bM2Impl(1)) + End Sub End Class End Namespace diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Retargeting/RetargetingTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Retargeting/RetargetingTests.vb index b4380d1e3bf67..b61ce12c60488 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Retargeting/RetargetingTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Retargeting/RetargetingTests.vb @@ -3255,6 +3255,40 @@ End Class Assert.True(DirectCast(cs, INamedTypeSymbol).IsSerializable) End Sub + + Public Sub ExplicitInterfaceImplementationRetargetingGenericType() + Dim source1 = " +Public Class C1(Of T) + Public Interface I1 + Sub M(x As T) + End Interface +End Class +" + Dim ref1 = CreateEmptyCompilation("").ToMetadataReference() + Dim compilation1 = CreateCompilation(source1, references:={ref1}) + + Dim source2 = " +Public Class C2(Of U) + Implements C1(Of U).I1 + + Sub M(x As U) Implements C1(Of U).I1.M + End Sub +End Class +" + Dim compilation2 = CreateCompilation(source2, references:={compilation1.ToMetadataReference(), ref1, CreateEmptyCompilation("").ToMetadataReference()}) + + Dim compilation3 = CreateCompilation("", references:={compilation1.ToMetadataReference(), compilation2.ToMetadataReference()}) + + Assert.NotSame(compilation2.GetTypeByMetadataName("C1`1"), compilation3.GetTypeByMetadataName("C1`1")) + + Dim c2 = compilation3.GetTypeByMetadataName("C2`1") + Assert.IsType(Of RetargetingNamedTypeSymbol)(c2) + + Dim m = c2.GetMethod("M") + + Assert.Equal(c2.Interfaces().Single().GetMethod("M"), m.ExplicitInterfaceImplementations.Single()) + End Sub + End Class #End If From c07b0585b09da096bc39efb8ec674c47b4dc1b8d Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Tue, 8 Jun 2021 13:48:42 -0700 Subject: [PATCH 093/127] Strengthen some unit-tests (#53949) --- .../StaticAbstractMembersInInterfacesTests.cs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index 6539084721f1d..3858ac8477928 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -6379,6 +6379,16 @@ .maxstack 1 var m02 = compilation1.GetMember("Test.M02"); Assert.Equal("void I1.M01()", ((CSharpSemanticModel)model).LookupSymbols(node.SpanStart, m02.TypeParameters[0], "M01").Single().ToTestDisplayString()); + Assert.Contains("void I1.M01()", ((CSharpSemanticModel)model).LookupSymbols(node.SpanStart, m02.TypeParameters[0]).ToTestDisplayStrings()); + Assert.Equal("void I1.M01()", ((CSharpSemanticModel)model).LookupStaticMembers(node.SpanStart, m02.TypeParameters[0], "M01").Single().ToTestDisplayString()); + Assert.Contains("void I1.M01()", ((CSharpSemanticModel)model).LookupStaticMembers(node.SpanStart, m02.TypeParameters[0]).ToTestDisplayStrings()); + + Assert.Equal("void I1.M01()", model.LookupSymbols(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol(), "M01").Single().ToTestDisplayString()); + Assert.Contains("void I1.M01()", model.LookupSymbols(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol()).ToTestDisplayStrings()); + Assert.Equal("void I1.M01()", model.LookupStaticMembers(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol(), "M01").Single().ToTestDisplayString()); + Assert.Contains("void I1.M01()", model.LookupStaticMembers(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol()).ToTestDisplayStrings()); + + Assert.Contains("M01", model.LookupNames(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol())); } [Fact] @@ -9999,6 +10009,16 @@ .maxstack 1 var m02 = compilation1.GetMember("Test.M02"); Assert.Equal("System.Int32 I1.P01 { get; set; }", ((CSharpSemanticModel)model).LookupSymbols(node.SpanStart, m02.TypeParameters[0], "P01").Single().ToTestDisplayString()); + Assert.Equal("System.Int32 I1.P01 { get; set; }", ((CSharpSemanticModel)model).LookupStaticMembers(node.SpanStart, m02.TypeParameters[0], "P01").Single().ToTestDisplayString()); + Assert.Contains("System.Int32 I1.P01 { get; set; }", ((CSharpSemanticModel)model).LookupSymbols(node.SpanStart, m02.TypeParameters[0]).ToTestDisplayStrings()); + Assert.Contains("System.Int32 I1.P01 { get; set; }", ((CSharpSemanticModel)model).LookupStaticMembers(node.SpanStart, m02.TypeParameters[0]).ToTestDisplayStrings()); + + Assert.Equal("System.Int32 I1.P01 { get; set; }", model.LookupSymbols(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol(), "P01").Single().ToTestDisplayString()); + Assert.Equal("System.Int32 I1.P01 { get; set; }", model.LookupStaticMembers(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol(), "P01").Single().ToTestDisplayString()); + Assert.Contains("System.Int32 I1.P01 { get; set; }", model.LookupSymbols(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol()).ToTestDisplayStrings()); + Assert.Contains("System.Int32 I1.P01 { get; set; }", model.LookupStaticMembers(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol()).ToTestDisplayStrings()); + + Assert.Contains("P01", model.LookupNames(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol())); } [Fact] @@ -10075,6 +10095,16 @@ .maxstack 1 var m02 = compilation1.GetMember("Test.M02"); Assert.Equal("System.Int32 I1.P01 { get; set; }", ((CSharpSemanticModel)model).LookupSymbols(node.SpanStart, m02.TypeParameters[0], "P01").Single().ToTestDisplayString()); + Assert.Equal("System.Int32 I1.P01 { get; set; }", ((CSharpSemanticModel)model).LookupStaticMembers(node.SpanStart, m02.TypeParameters[0], "P01").Single().ToTestDisplayString()); + Assert.Contains("System.Int32 I1.P01 { get; set; }", ((CSharpSemanticModel)model).LookupSymbols(node.SpanStart, m02.TypeParameters[0]).ToTestDisplayStrings()); + Assert.Contains("System.Int32 I1.P01 { get; set; }", ((CSharpSemanticModel)model).LookupStaticMembers(node.SpanStart, m02.TypeParameters[0]).ToTestDisplayStrings()); + + Assert.Equal("System.Int32 I1.P01 { get; set; }", model.LookupSymbols(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol(), "P01").Single().ToTestDisplayString()); + Assert.Equal("System.Int32 I1.P01 { get; set; }", model.LookupStaticMembers(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol(), "P01").Single().ToTestDisplayString()); + Assert.Contains("System.Int32 I1.P01 { get; set; }", model.LookupSymbols(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol()).ToTestDisplayStrings()); + Assert.Contains("System.Int32 I1.P01 { get; set; }", model.LookupStaticMembers(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol()).ToTestDisplayStrings()); + + Assert.Contains("P01", model.LookupNames(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol())); } [Fact] @@ -10187,6 +10217,16 @@ .maxstack 1 var m02 = compilation1.GetMember("Test.M02"); Assert.Equal("System.Int32 I1.P01 { get; set; }", ((CSharpSemanticModel)model).LookupSymbols(node.SpanStart, m02.TypeParameters[0], "P01").Single().ToTestDisplayString()); + Assert.Equal("System.Int32 I1.P01 { get; set; }", ((CSharpSemanticModel)model).LookupStaticMembers(node.SpanStart, m02.TypeParameters[0], "P01").Single().ToTestDisplayString()); + Assert.Contains("System.Int32 I1.P01 { get; set; }", ((CSharpSemanticModel)model).LookupSymbols(node.SpanStart, m02.TypeParameters[0]).ToTestDisplayStrings()); + Assert.Contains("System.Int32 I1.P01 { get; set; }", ((CSharpSemanticModel)model).LookupStaticMembers(node.SpanStart, m02.TypeParameters[0]).ToTestDisplayStrings()); + + Assert.Equal("System.Int32 I1.P01 { get; set; }", model.LookupSymbols(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol(), "P01").Single().ToTestDisplayString()); + Assert.Equal("System.Int32 I1.P01 { get; set; }", model.LookupStaticMembers(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol(), "P01").Single().ToTestDisplayString()); + Assert.Contains("System.Int32 I1.P01 { get; set; }", model.LookupSymbols(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol()).ToTestDisplayStrings()); + Assert.Contains("System.Int32 I1.P01 { get; set; }", model.LookupStaticMembers(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol()).ToTestDisplayStrings()); + + Assert.Contains("P01", model.LookupNames(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol())); } [Fact] @@ -10863,6 +10903,16 @@ .maxstack 1 var m02 = compilation1.GetMember("Test.M02"); Assert.Equal("event System.Action I1.E01", ((CSharpSemanticModel)model).LookupSymbols(node.SpanStart, m02.TypeParameters[0], "E01").Single().ToTestDisplayString()); + Assert.Equal("event System.Action I1.E01", ((CSharpSemanticModel)model).LookupStaticMembers(node.SpanStart, m02.TypeParameters[0], "E01").Single().ToTestDisplayString()); + Assert.Contains("event System.Action I1.E01", ((CSharpSemanticModel)model).LookupSymbols(node.SpanStart, m02.TypeParameters[0]).ToTestDisplayStrings()); + Assert.Contains("event System.Action I1.E01", ((CSharpSemanticModel)model).LookupStaticMembers(node.SpanStart, m02.TypeParameters[0]).ToTestDisplayStrings()); + + Assert.Equal("event System.Action I1.E01", model.LookupSymbols(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol(), "E01").Single().ToTestDisplayString()); + Assert.Equal("event System.Action I1.E01", model.LookupStaticMembers(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol(), "E01").Single().ToTestDisplayString()); + Assert.Contains("event System.Action I1.E01", model.LookupSymbols(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol()).ToTestDisplayStrings()); + Assert.Contains("event System.Action I1.E01", model.LookupStaticMembers(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol()).ToTestDisplayStrings()); + + Assert.Contains("E01", model.LookupNames(node.SpanStart, m02.TypeParameters[0].GetPublicSymbol())); } [Fact] From 62b550b18f138a7195074314bed6fe00bd14e68b Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Tue, 8 Jun 2021 11:41:49 -0700 Subject: [PATCH 094/127] Reference the new helpers --- .../AbstractInheritanceMarginService.cs | 4 +- .../InheritanceMarginServiceHelpers.cs | 85 ++++++++++++------- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs b/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs index cd522588ef52f..161ec6d3bc76c 100644 --- a/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs +++ b/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs @@ -92,7 +92,9 @@ private static bool CanHaveInheritanceTarget(ISymbol symbol) return false; } - if (symbol is INamedTypeSymbol or IEventSymbol or IPropertySymbol || + if (symbol is INamedTypeSymbol { TypeKind: TypeKind.Interface or TypeKind.Class or TypeKind.Struct } + or IEventSymbol + or IPropertySymbol || symbol.IsOrdinaryMethod()) { return true; diff --git a/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs b/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs index bbc2323ae646c..6a0517a9c7eb0 100644 --- a/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs +++ b/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs @@ -21,6 +21,9 @@ namespace Microsoft.CodeAnalysis.InheritanceMargin { internal static class InheritanceMarginServiceHelper { + /// + /// Display format used to generated the target's diplay name. + /// private static readonly SymbolDisplayFormat s_displayFormat = new( globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining, typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes, @@ -79,6 +82,7 @@ private static async ValueTask foreach (var (symbolKey, lineNumber) in symbolKeyAndLineNumbers) { var symbol = symbolKey.Resolve(compilation, cancellationToken: cancellationToken).Symbol; + if (symbol is INamedTypeSymbol namedTypeSymbol) { await AddInheritanceMemberItemsForNamedTypeAsync(solution, namedTypeSymbol, lineNumber, builder, cancellationToken).ConfigureAwait(false); @@ -127,14 +131,28 @@ private static async ValueTask AddInheritanceMemberItemsForNamedTypeAsync( if (baseSymbols.Any() || derivedSymbols.Any()) { - var item = await CreateInheritanceMemberItemForNamedTypeAsync( - solution, - memberSymbol, - lineNumber, - baseSymbols: baseSymbols.CastArray(), - derivedTypesSymbols: derivedSymbols.CastArray(), - cancellationToken).ConfigureAwait(false); - builder.AddIfNotNull(item); + if (memberSymbol.TypeKind == TypeKind.Interface) + { + var item = await CreateInheritanceMemberItemForInterfaceAsync( + solution, + memberSymbol, + lineNumber, + baseSymbols: baseSymbols.CastArray(), + derivedTypesSymbols: derivedSymbols.CastArray(), + cancellationToken).ConfigureAwait(false); + builder.AddIfNotNull(item); + } + else + { + var item = await CreateInheritanceMemberItemForClassAndStructureAsync( + solution, + memberSymbol, + lineNumber, + baseSymbols: baseSymbols.CastArray(), + derivedTypesSymbols: derivedSymbols.CastArray(), + cancellationToken).ConfigureAwait(false); + builder.AddIfNotNull(item); + } } } @@ -172,21 +190,35 @@ private static async ValueTask AddInheritanceMemberItemsForClassAndStructMembers if (overriddenSymbols.Any() || overridingSymbols.Any() || implementingSymbols.Any() || implementedSymbols.Any()) { - var item = await CreateInheritanceMemberInfoForMemberAsync( - solution, - memberSymbol, - lineNumber, - implementingMembers: implementingSymbols, - implementedMembers: implementedSymbols, - overridenMembers: overriddenSymbols, - overridingMembers: overridingSymbols, - cancellationToken).ConfigureAwait(false); - - builder.AddIfNotNull(item); + if (memberSymbol.ContainingSymbol.IsInterfaceType()) + { + var item = await CreateInheritanceMemberInfoForInterfaceMemberAsync(solution, + memberSymbol, + lineNumber, + implementedMembers: implementedSymbols, + overridenMembers: overriddenSymbols, + overridingMembers: overridingSymbols, + cancellationToken).ConfigureAwait(false); + + builder.AddIfNotNull(item); + } + else + { + var item = await CreateInheritanceMemberInfoForClassOrStructMemberAsync(solution, + memberSymbol, + lineNumber, + implementingMembers: implementingSymbols, + overridenMembers: overriddenSymbols, + overridingMembers: overridingSymbols, + cancellationToken).ConfigureAwait(false); + + builder.AddIfNotNull(item); + } } } #region Interface + private static async ValueTask CreateInheritanceMemberItemForInterfaceAsync( Solution solution, INamedTypeSymbol interfaceSymbol, @@ -226,21 +258,11 @@ private static async ValueTask CreateInherita Solution solution, ISymbol memberSymbol, int lineNumber, - ImmutableArray implementingMembers, ImmutableArray implementedMembers, ImmutableArray overridenMembers, ImmutableArray overridingMembers, CancellationToken cancellationToken) { - var implementingMemberItems = await implementingMembers - .SelectAsArray(symbol => symbol.OriginalDefinition) - .Distinct() - .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( - solution, - symbol, - InheritanceRelationship.ImplmentedMember, - cancellationToken), cancellationToken).ConfigureAwait(false); - var implementedMemberItems = await implementedMembers .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() @@ -272,7 +294,7 @@ private static async ValueTask CreateInherita lineNumber, FindUsagesHelpers.GetDisplayParts(memberSymbol), memberSymbol.GetGlyph(), - implementingMemberItems.Concat(implementedMemberItems) + implementedMemberItems .Concat(overridenMemberItems) .Concat(overridingMemberItems)); } @@ -372,7 +394,6 @@ private static async ValueTask CreateInherita .Concat(overridingMemberItems)); } #endregion - private static async ValueTask CreateInheritanceMemberItemForNamedTypeAsync( Solution solution, INamedTypeSymbol memberSymbol, @@ -532,7 +553,7 @@ private static async Task> GetImplementedSymbolsForTypeM CancellationToken cancellationToken) { if (memberSymbol is IMethodSymbol or IEventSymbol or IPropertySymbol - && memberSymbol.ContainingSymbol.IsInterfaceType()) + && memberSymbol.ContainingSymbol.IsInterfaceType()) { using var _ = ArrayBuilder.GetInstance(out var builder); // 1. Find all direct implementations for this member From 72057b98fb04b388ece55ef8360e7b2fed4a484a Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Tue, 8 Jun 2021 15:15:38 -0700 Subject: [PATCH 095/127] Add relevant checks for tuple comparison. (#53871) Closes #53797. Closes #53799. --- .../Portable/Binder/Binder_Conversions.cs | 35 +- .../Portable/Binder/Binder_Operators.cs | 5 + .../Portable/Binder/Binder_TupleOperators.cs | 4 +- .../Portable/Binder/ForEachLoopBinder.cs | 1 + .../LocalRewriter_TupleBinaryOperator.cs | 2 - .../StaticAbstractMembersInInterfacesTests.cs | 1125 ++++++++++++++++- 6 files changed, 1133 insertions(+), 39 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index f1de381c149ec..5df84fbfefb42 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -90,6 +90,7 @@ protected BoundExpression CreateConversion( // Obsolete diagnostics for method group are reported as part of creating the method group conversion. ReportDiagnosticsIfObsolete(diagnostics, conversion, syntax, hasBaseReceiver: false); + CheckConstraintLanguageVersionAndRuntimeSupportForConversion(syntax, conversion, diagnostics); if (conversion.IsAnonymousFunction && source.Kind == BoundKind.UnboundLambda) { @@ -214,6 +215,25 @@ protected BoundExpression CreateConversion( { WasCompilerGenerated = wasCompilerGenerated }; } + internal void CheckConstraintLanguageVersionAndRuntimeSupportForConversion(SyntaxNodeOrToken syntax, Conversion conversion, BindingDiagnosticBag diagnostics) + { + if (conversion.IsUserDefined && conversion.Method is MethodSymbol method && method.IsStatic && method.IsAbstract) + { + Debug.Assert(conversion.ConstrainedToTypeOpt is TypeParameterSymbol); + + if (Compilation.SourceModule != method.ContainingModule) + { + Debug.Assert(syntax.SyntaxTree is object); + CheckFeatureAvailability(syntax.SyntaxTree, MessageID.IDS_FeatureStaticAbstractMembersInInterfaces, diagnostics, syntax.GetLocation()!); + + if (!Compilation.Assembly.RuntimeSupportsStaticAbstractMembersInInterfaces) + { + Error(diagnostics, ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, syntax); + } + } + } + } + private BoundExpression ConvertObjectCreationExpression(SyntaxNode syntax, BoundUnconvertedObjectCreationExpression node, bool isCast, TypeSymbol destination, BindingDiagnosticBag diagnostics) { var arguments = AnalyzedArguments.GetInstance(node.Arguments, node.ArgumentRefKindsOpt, node.ArgumentNamesOpt); @@ -366,21 +386,6 @@ private BoundExpression CreateUserDefinedConversion( { WasCompilerGenerated = source.WasCompilerGenerated }; } - if (conversion.Method is MethodSymbol method && method.IsStatic && method.IsAbstract) - { - Debug.Assert(conversion.ConstrainedToTypeOpt is TypeParameterSymbol); - - if (Compilation.SourceModule != method.ContainingModule) - { - CheckFeatureAvailability(syntax, MessageID.IDS_FeatureStaticAbstractMembersInInterfaces, diagnostics); - - if (!Compilation.Assembly.RuntimeSupportsStaticAbstractMembersInInterfaces) - { - Error(diagnostics, ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, syntax); - } - } - } - // Due to an oddity in the way we create a non-lifted user-defined conversion from A to D? // (required backwards compatibility with the native compiler) we can end up in a situation // where we have: diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index c7ce32b1dd2da..0e4bcb0bbecad 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -197,6 +197,7 @@ private BoundExpression BindCompoundAssignment(AssignmentExpressionSyntax node, else { ReportDiagnosticsIfObsolete(diagnostics, finalConversion, node, hasBaseReceiver: false); + CheckConstraintLanguageVersionAndRuntimeSupportForConversion(node, finalConversion, diagnostics); } if (finalConversion.IsExplicit && @@ -225,6 +226,7 @@ private BoundExpression BindCompoundAssignment(AssignmentExpressionSyntax node, Conversion leftConversion = best.LeftConversion; ReportDiagnosticsIfObsolete(diagnostics, leftConversion, node, hasBaseReceiver: false); + CheckConstraintLanguageVersionAndRuntimeSupportForConversion(node, leftConversion, diagnostics); return new BoundCompoundAssignmentOperator(node, bestSignature, left, rightConverted, leftConversion, finalConversion, resultKind, originalUserDefinedOperators, leftType, hasError); @@ -2173,6 +2175,7 @@ private BoundExpression BindIncrementOperator(CSharpSyntaxNode node, ExpressionS else { ReportDiagnosticsIfObsolete(diagnostics, resultConversion, node, hasBaseReceiver: false); + CheckConstraintLanguageVersionAndRuntimeSupportForConversion(node, resultConversion, diagnostics); } if (!hasErrors && operandType.IsVoidPointer()) @@ -2184,6 +2187,7 @@ private BoundExpression BindIncrementOperator(CSharpSyntaxNode node, ExpressionS Conversion operandConversion = best.Conversion; ReportDiagnosticsIfObsolete(diagnostics, operandConversion, node, hasBaseReceiver: false); + CheckConstraintLanguageVersionAndRuntimeSupportForConversion(node, operandConversion, diagnostics); return new BoundIncrementOperator( node, @@ -3844,6 +3848,7 @@ private BoundExpression BindNullCoalescingOperator(BinaryExpressionSyntax node, else { ReportDiagnosticsIfObsolete(diagnostics, leftConversion, node, hasBaseReceiver: false); + CheckConstraintLanguageVersionAndRuntimeSupportForConversion(node, leftConversion, diagnostics); } diagnostics.Add(node, useSiteInfo); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs index ab0ca36302020..27dc594d30ae5 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs @@ -104,7 +104,8 @@ private TupleBinaryOperatorInfo BindTupleBinaryOperatorInfo(BinaryExpressionSynt case BoundBinaryOperator binary: PrepareBoolConversionAndTruthOperator(binary.Type, node, kind, diagnostics, out Conversion conversionIntoBoolOperator, out UnaryOperatorSignature boolOperator); - // https://github.com/dotnet/roslyn/issues/53797: Ensure we have a unit-test for this code path. + CheckConstraintLanguageVersionAndRuntimeSupportForOperator(node, boolOperator.Method, boolOperator.ConstrainedToTypeOpt, diagnostics); + return new TupleBinaryOperatorInfo.Single(binary.Left.Type, binary.Right.Type, binary.OperatorKind, binary.MethodOpt, binary.ConstrainedToTypeOpt, conversionIntoBoolOperator, boolOperator); default: @@ -131,6 +132,7 @@ private void PrepareBoolConversionAndTruthOperator(TypeSymbol type, BinaryExpres if (conversion.IsImplicit) { ReportDiagnosticsIfObsolete(diagnostics, conversion, node, hasBaseReceiver: false); + CheckConstraintLanguageVersionAndRuntimeSupportForConversion(node, conversion, diagnostics); conversionForBool = conversion; boolOperator = default; return; diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index aac4b1ea9ebde..8b26cf2102d6f 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -447,6 +447,7 @@ private BoundForEachStatement BindForEachPartsWorker(BindingDiagnosticBag diagno else { ReportDiagnosticsIfObsolete(diagnostics, elementConversion, _syntax.ForEachKeyword, hasBaseReceiver: false); + CheckConstraintLanguageVersionAndRuntimeSupportForConversion(_syntax.ForEachKeyword, elementConversion, diagnostics); } // Spec (§8.8.4): diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_TupleBinaryOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_TupleBinaryOperator.cs index 35e9687d694f1..7e24158b4dbc7 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_TupleBinaryOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_TupleBinaryOperator.cs @@ -563,7 +563,6 @@ private BoundExpression RewriteTupleSingleOperator(TupleBinaryOperatorInfo.Singl return new BoundLiteral(left.Syntax, ConstantValue.Create(operatorKind == BinaryOperatorKind.Equal), boolType); } - // https://github.com/dotnet/roslyn/issues/53799: Make sure we have test coverage for this code path BoundExpression binary = MakeBinaryOperator(_factory.Syntax, single.Kind, left, right, single.MethodSymbolOpt?.ReturnType ?? boolType, single.MethodSymbolOpt, single.ConstrainedToTypeOpt); UnaryOperatorSignature boolOperator = single.BoolOperator; Conversion boolConversion = single.ConversionForBool; @@ -577,7 +576,6 @@ private BoundExpression RewriteTupleSingleOperator(TupleBinaryOperatorInfo.Singl BoundExpression convertedBinary = MakeConversionNode(_factory.Syntax, binary, boolConversion, boolOperator.OperandType, @checked: false); Debug.Assert(boolOperator.ReturnType.SpecialType == SpecialType.System_Boolean); - // https://github.com/dotnet/roslyn/issues/53799: Make sure we have test coverage for this code path result = MakeUnaryOperator(boolOperator.Kind, binary.Syntax, boolOperator.Method, boolOperator.ConstrainedToTypeOpt, convertedBinary, boolType); if (operatorKind == BinaryOperatorKind.Equal) diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index 3858ac8477928..a6badda285516 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -7293,6 +7293,384 @@ static void M02(T x) where T : I1 ); } + [Theory] + [CombinatorialData] + public void ConsumeAbstractTrueFalseOperatorForTupleEquality_01([CombinatorialValues("==", "!=")] string op) + { + var source1 = +@" +interface I1 +{ + abstract static bool operator true (I1 x); + abstract static bool operator false (I1 x); + + static void M02((int, C) x) + { + _ = x " + op + @" x; + } + + void M03((int, C) y) + { + _ = y " + op + @" y; + } +} + +class Test +{ + static void MT1((int, C) a) + { + _ = a " + op + @" a; + } + + static void MT2() where T : I1 + { + _ = (System.Linq.Expressions.Expression)>>)(((int, C) b) => (b " + op + @" b).ToString()); + } +} + +#pragma warning disable CS0660 // 'C' defines operator == or operator != but does not override Object.Equals(object o) +#pragma warning disable CS0661 // 'C' defines operator == or operator != but does not override Object.GetHashCode() + +class C +{ + public static T operator == (C x, C y) => default; + public static T operator != (C x, C y) => default; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (9,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // _ = x == x; + Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "x " + op + " x").WithLocation(9, 13), + // (14,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // _ = y == y; + Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "y " + op + " y").WithLocation(14, 13), + // (22,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // _ = a == a; + Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "a " + op + " a").WithLocation(22, 13), + // (27,98): error CS8382: An expression tree may not contain a tuple == or != operator + // _ = (System.Linq.Expressions.Expression)>>)(((int, C) b) => (b == b).ToString()); + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsTupleBinOp, "b " + op + " b").WithLocation(27, 98) + ); + } + + [Theory] + [CombinatorialData] + public void ConsumeAbstractTrueFalseOperatorForTupleEquality_03([CombinatorialValues("==", "!=")] string op) + { + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static bool operator true (T x); + abstract static bool operator false (T x); +} + +class Test +{ + static void M02((int, C) x) where T : U where U : I1 + { + _ = x " + op + @" x; + } +} + +#pragma warning disable CS0660 // 'C' defines operator == or operator != but does not override Object.Equals(object o) +#pragma warning disable CS0661 // 'C' defines operator == or operator != but does not override Object.GetHashCode() + +class C +{ + public static T operator == (C x, C y) => default; + public static T operator != (C x, C y) => default; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + if (op == "==") + { + verifier.VerifyIL("Test.M02(System.ValueTuple>)", +@" +{ + // Code size 55 (0x37) + .maxstack 2 + .locals init (System.ValueTuple> V_0, + System.ValueTuple> V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldarg.0 + IL_0004: stloc.1 + IL_0005: ldloc.0 + IL_0006: ldfld ""int System.ValueTuple>.Item1"" + IL_000b: ldloc.1 + IL_000c: ldfld ""int System.ValueTuple>.Item1"" + IL_0011: bne.un.s IL_0034 + IL_0013: ldloc.0 + IL_0014: ldfld ""C System.ValueTuple>.Item2"" + IL_0019: ldloc.1 + IL_001a: ldfld ""C System.ValueTuple>.Item2"" + IL_001f: call ""T C.op_Equality(C, C)"" + IL_0024: constrained. ""T"" + IL_002a: call ""bool I1.op_False(T)"" + IL_002f: ldc.i4.0 + IL_0030: ceq + IL_0032: br.s IL_0035 + IL_0034: ldc.i4.0 + IL_0035: pop + IL_0036: ret +} +"); + } + else + { + verifier.VerifyIL("Test.M02(System.ValueTuple>)", +@" +{ + // Code size 52 (0x34) + .maxstack 2 + .locals init (System.ValueTuple> V_0, + System.ValueTuple> V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldarg.0 + IL_0004: stloc.1 + IL_0005: ldloc.0 + IL_0006: ldfld ""int System.ValueTuple>.Item1"" + IL_000b: ldloc.1 + IL_000c: ldfld ""int System.ValueTuple>.Item1"" + IL_0011: bne.un.s IL_0031 + IL_0013: ldloc.0 + IL_0014: ldfld ""C System.ValueTuple>.Item2"" + IL_0019: ldloc.1 + IL_001a: ldfld ""C System.ValueTuple>.Item2"" + IL_001f: call ""T C.op_Inequality(C, C)"" + IL_0024: constrained. ""T"" + IL_002a: call ""bool I1.op_True(T)"" + IL_002f: br.s IL_0032 + IL_0031: ldc.i4.1 + IL_0032: pop + IL_0033: ret +} +"); + } + + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + + if (op == "==") + { + verifier.VerifyIL("Test.M02(System.ValueTuple>)", +@" +{ + // Code size 54 (0x36) + .maxstack 2 + .locals init (System.ValueTuple> V_0, + System.ValueTuple> V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldarg.0 + IL_0003: stloc.1 + IL_0004: ldloc.0 + IL_0005: ldfld ""int System.ValueTuple>.Item1"" + IL_000a: ldloc.1 + IL_000b: ldfld ""int System.ValueTuple>.Item1"" + IL_0010: bne.un.s IL_0033 + IL_0012: ldloc.0 + IL_0013: ldfld ""C System.ValueTuple>.Item2"" + IL_0018: ldloc.1 + IL_0019: ldfld ""C System.ValueTuple>.Item2"" + IL_001e: call ""T C.op_Equality(C, C)"" + IL_0023: constrained. ""T"" + IL_0029: call ""bool I1.op_False(T)"" + IL_002e: ldc.i4.0 + IL_002f: ceq + IL_0031: br.s IL_0034 + IL_0033: ldc.i4.0 + IL_0034: pop + IL_0035: ret +} +"); + } + else + { + verifier.VerifyIL("Test.M02(System.ValueTuple>)", +@" +{ + // Code size 51 (0x33) + .maxstack 2 + .locals init (System.ValueTuple> V_0, + System.ValueTuple> V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldarg.0 + IL_0003: stloc.1 + IL_0004: ldloc.0 + IL_0005: ldfld ""int System.ValueTuple>.Item1"" + IL_000a: ldloc.1 + IL_000b: ldfld ""int System.ValueTuple>.Item1"" + IL_0010: bne.un.s IL_0030 + IL_0012: ldloc.0 + IL_0013: ldfld ""C System.ValueTuple>.Item2"" + IL_0018: ldloc.1 + IL_0019: ldfld ""C System.ValueTuple>.Item2"" + IL_001e: call ""T C.op_Inequality(C, C)"" + IL_0023: constrained. ""T"" + IL_0029: call ""bool I1.op_True(T)"" + IL_002e: br.s IL_0031 + IL_0030: ldc.i4.1 + IL_0031: pop + IL_0032: ret +} +"); + } + + var tree = compilation1.SyntaxTrees.Single(); + var model = compilation1.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().First(); + + Assert.Equal("x " + op + " x", node.ToString()); + VerifyOperationTreeForNode(compilation1, model, node, +// Information about user-defined operators isn't exposed today. +@" +ITupleBinaryOperation (BinaryOperatorKind." + (op == "==" ? "Equals" : "NotEquals") + @") (OperationKind.TupleBinary, Type: System.Boolean) (Syntax: 'x " + op + @" x') + Left: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: (System.Int32, C)) (Syntax: 'x') + Right: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: (System.Int32, C)) (Syntax: 'x') +"); + } + + [Theory] + [CombinatorialData] + public void ConsumeAbstractTrueFalseOperatorForTupleEquality_04([CombinatorialValues("==", "!=")] string op) + { + var source1 = +@" +public interface I1 +{ + abstract static bool operator true (I1 x); + abstract static bool operator false (I1 x); +} +"; + var source2 = +@" +class Test +{ + static void M02((int, C) x) where T : I1 + { + _ = x " + op + @" x; + } +} + +#pragma warning disable CS0660 // 'C' defines operator == or operator != but does not override Object.Equals(object o) +#pragma warning disable CS0661 // 'C' defines operator == or operator != but does not override Object.GetHashCode() + +class C +{ + public static T operator == (C x, C y) => default; + public static T operator != (C x, C y) => default; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (6,13): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // _ = x == x; + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "x " + op + " x").WithLocation(6, 13) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation3.VerifyDiagnostics( + // (21,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static bool operator true (I1 x); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "true").WithLocation(21, 35), + // (22,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static bool operator false (I1 x); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "false").WithLocation(22, 35) + ); + } + + [Theory] + [CombinatorialData] + public void ConsumeAbstractTrueFalseOperatorForTupleEquality_06([CombinatorialValues("==", "!=")] string op) + { + var source1 = +@" +public interface I1 +{ + abstract static bool operator true (I1 x); + abstract static bool operator false (I1 x); +} +"; + var source2 = +@" +class Test +{ + static void M02((int, C) x) where T : I1 + { + _ = x " + op + @" x; + } +} + +#pragma warning disable CS0660 // 'C' defines operator == or operator != but does not override Object.Equals(object o) +#pragma warning disable CS0661 // 'C' defines operator == or operator != but does not override Object.GetHashCode() + +class C +{ + public static T operator == (C x, C y) => default; + public static T operator != (C x, C y) => default; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (6,13): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // _ = x == x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "x " + op + " x").WithArguments("static abstract members in interfaces").WithLocation(6, 13) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation3.VerifyDiagnostics( + // (21,35): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static bool operator true (I1 x); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "true").WithArguments("abstract", "9.0", "preview").WithLocation(21, 35), + // (22,35): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static bool operator false (I1 x); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "false").WithArguments("abstract", "9.0", "preview").WithLocation(22, 35) + ); + } + [Theory] [CombinatorialData] public void ConsumeAbstractBinaryOperator_01([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op) @@ -7706,6 +8084,64 @@ private static string BinaryOperatorKind(string op) throw TestExceptionUtilities.UnexpectedValue(op); } + [Theory] + [CombinatorialData] + public void ConsumeAbstractBinaryOperatorForTupleEquality_01([CombinatorialValues("==", "!=")] string op) + { + var source1 = +@" +interface I1 where T : I1 +{ + abstract static bool operator == (T x, T y); + abstract static bool operator != (T x, T y); + + abstract static bool operator == (I1 x, I1 y); + abstract static bool operator != (I1 x, I1 y); + + static void M02((int, I1) x) + { + _ = x " + op + @" x; + } + + void M03((int, I1) y) + { + _ = y " + op + @" y; + } +} + +class Test +{ + static void MT1((int, I1) a) where T : I1 + { + _ = a " + op + @" a; + } + + static void MT2() where T : I1 + { + _ = (System.Linq.Expressions.Expression>)(((int, T) b) => (b " + op + @" b).ToString()); + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (12,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // _ = x == x; + Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "x " + op + " x").WithLocation(12, 13), + // (17,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // _ = y == y; + Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "y " + op + " y").WithLocation(17, 13), + // (25,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // _ = a == a; + Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "a " + op + " a").WithLocation(25, 13), + // (30,92): error CS8382: An expression tree may not contain a tuple == or != operator + // _ = (System.Linq.Expressions.Expression>)(((int, T) b) => (b == b).ToString()); + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsTupleBinOp, "b " + op + " b").WithLocation(30, 92) + ); + } + [Theory] [CombinatorialData] public void ConsumeAbstractBinaryOperator_03([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>")] string op) @@ -9201,6 +9637,183 @@ .locals init (T? V_0, "); } + [Theory] + [CombinatorialData] + public void ConsumeAbstractBinaryOperatorForTupleEquality_03([CombinatorialValues("==", "!=")] string op) + { + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static bool operator == (T x, T y); + abstract static bool operator != (T x, T y); +} + +class Test +{ + static void M02((int, T) x) where T : U where U : I1 + { + _ = x " + op + @" x; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + if (op == "==") + { + verifier.VerifyIL("Test.M02(System.ValueTuple)", +@" +{ + // Code size 47 (0x2f) + .maxstack 2 + .locals init (System.ValueTuple V_0, + System.ValueTuple V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldarg.0 + IL_0004: stloc.1 + IL_0005: ldloc.0 + IL_0006: ldfld ""int System.ValueTuple.Item1"" + IL_000b: ldloc.1 + IL_000c: ldfld ""int System.ValueTuple.Item1"" + IL_0011: bne.un.s IL_002c + IL_0013: ldloc.0 + IL_0014: ldfld ""T System.ValueTuple.Item2"" + IL_0019: ldloc.1 + IL_001a: ldfld ""T System.ValueTuple.Item2"" + IL_001f: constrained. ""T"" + IL_0025: call ""bool I1.op_Equality(T, T)"" + IL_002a: br.s IL_002d + IL_002c: ldc.i4.0 + IL_002d: pop + IL_002e: ret +} +"); + } + else + { + verifier.VerifyIL("Test.M02(System.ValueTuple)", +@" +{ + // Code size 47 (0x2f) + .maxstack 2 + .locals init (System.ValueTuple V_0, + System.ValueTuple V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldarg.0 + IL_0004: stloc.1 + IL_0005: ldloc.0 + IL_0006: ldfld ""int System.ValueTuple.Item1"" + IL_000b: ldloc.1 + IL_000c: ldfld ""int System.ValueTuple.Item1"" + IL_0011: bne.un.s IL_002c + IL_0013: ldloc.0 + IL_0014: ldfld ""T System.ValueTuple.Item2"" + IL_0019: ldloc.1 + IL_001a: ldfld ""T System.ValueTuple.Item2"" + IL_001f: constrained. ""T"" + IL_0025: call ""bool I1.op_Inequality(T, T)"" + IL_002a: br.s IL_002d + IL_002c: ldc.i4.1 + IL_002d: pop + IL_002e: ret +} +"); + } + + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + + if (op == "==") + { + verifier.VerifyIL("Test.M02(System.ValueTuple)", +@" +{ + // Code size 46 (0x2e) + .maxstack 2 + .locals init (System.ValueTuple V_0, + System.ValueTuple V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldarg.0 + IL_0003: stloc.1 + IL_0004: ldloc.0 + IL_0005: ldfld ""int System.ValueTuple.Item1"" + IL_000a: ldloc.1 + IL_000b: ldfld ""int System.ValueTuple.Item1"" + IL_0010: bne.un.s IL_002b + IL_0012: ldloc.0 + IL_0013: ldfld ""T System.ValueTuple.Item2"" + IL_0018: ldloc.1 + IL_0019: ldfld ""T System.ValueTuple.Item2"" + IL_001e: constrained. ""T"" + IL_0024: call ""bool I1.op_Equality(T, T)"" + IL_0029: br.s IL_002c + IL_002b: ldc.i4.0 + IL_002c: pop + IL_002d: ret +} +"); + } + else + { + verifier.VerifyIL("Test.M02(System.ValueTuple)", +@" +{ + // Code size 46 (0x2e) + .maxstack 2 + .locals init (System.ValueTuple V_0, + System.ValueTuple V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldarg.0 + IL_0003: stloc.1 + IL_0004: ldloc.0 + IL_0005: ldfld ""int System.ValueTuple.Item1"" + IL_000a: ldloc.1 + IL_000b: ldfld ""int System.ValueTuple.Item1"" + IL_0010: bne.un.s IL_002b + IL_0012: ldloc.0 + IL_0013: ldfld ""T System.ValueTuple.Item2"" + IL_0018: ldloc.1 + IL_0019: ldfld ""T System.ValueTuple.Item2"" + IL_001e: constrained. ""T"" + IL_0024: call ""bool I1.op_Inequality(T, T)"" + IL_0029: br.s IL_002c + IL_002b: ldc.i4.1 + IL_002c: pop + IL_002d: ret +} +"); + } + + var tree = compilation1.SyntaxTrees.Single(); + var model = compilation1.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().First(); + + Assert.Equal("x " + op + " x", node.ToString()); + VerifyOperationTreeForNode(compilation1, model, node, +// Information about user-defined operators isn't exposed today. +@" +ITupleBinaryOperation (BinaryOperatorKind." + (op == "==" ? "Equals" : "NotEquals") + @") (OperationKind.TupleBinary, Type: System.Boolean) (Syntax: 'x " + op + @" x') + Left: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: (System.Int32, T)) (Syntax: 'x') + Right: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: (System.Int32, T)) (Syntax: 'x') +"); + } + [Theory] [CombinatorialData] public void ConsumeAbstractBinaryOperator_04([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op) @@ -9392,6 +10005,57 @@ static void M02(T x, int y) where T : I1 ); } + [Theory] + [CombinatorialData] + public void ConsumeAbstractBinaryOperatorForTupleEquality_04([CombinatorialValues("==", "!=")] string op) + { + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static bool operator == (T x, T y); + abstract static bool operator != (T x, T y); +} +"; + var source2 = +@" +class Test +{ + static void M02((int, T) x) where T : I1 + { + _ = x " + op + @" x; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (6,13): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // _ = x == x; + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "x " + op + " x").WithLocation(6, 13) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation3.VerifyDiagnostics( + // (12,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static bool operator == (T x, T y); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "==").WithLocation(12, 35), + // (13,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static bool operator != (T x, T y); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "!=").WithLocation(13, 35) + ); + } + [Theory] [CombinatorialData] public void ConsumeAbstractBinaryOperator_06([CombinatorialValues("+", "-", "*", "/", "%", "&", "|", "^", "<<", ">>", "<", ">", "<=", ">=", "==", "!=")] string op) @@ -9524,36 +10188,84 @@ static void M02(T x, T y) where T : I1 ); } - compilation3.VerifyDiagnostics(builder.ToArrayAndFree()); + compilation3.VerifyDiagnostics(builder.ToArrayAndFree()); + } + + [Theory] + [InlineData("+")] + [InlineData("-")] + [InlineData("*")] + [InlineData("/")] + [InlineData("%")] + [InlineData("&")] + [InlineData("|")] + [InlineData("^")] + [InlineData("<<")] + [InlineData(">>")] + public void ConsumeAbstractCompoundBinaryOperator_06(string op) + { + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static T operator" + op + @" (T x, int y); +} +"; + var source2 = +@" +class Test +{ + static void M02(T x, int y) where T : I1 + { + x " + op + @"= y; + } +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (6,9): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // x <<= y; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "x " + op + "= y").WithArguments("static abstract members in interfaces").WithLocation(6, 9) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation3.VerifyDiagnostics( + // (12,31): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static T operator<< (T x, int y); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, op).WithArguments("abstract", "9.0", "preview").WithLocation(12, 31) + ); } [Theory] - [InlineData("+")] - [InlineData("-")] - [InlineData("*")] - [InlineData("/")] - [InlineData("%")] - [InlineData("&")] - [InlineData("|")] - [InlineData("^")] - [InlineData("<<")] - [InlineData(">>")] - public void ConsumeAbstractCompoundBinaryOperator_06(string op) + [CombinatorialData] + public void ConsumeAbstractBinaryOperatorForTupleEquality_06([CombinatorialValues("==", "!=")] string op) { var source1 = @" public interface I1 where T : I1 { - abstract static T operator" + op + @" (T x, int y); + abstract static bool operator == (T x, T y); + abstract static bool operator != (T x, T y); } "; var source2 = @" class Test { - static void M02(T x, int y) where T : I1 + static void M02((int, T) x) where T : I1 { - x " + op + @"= y; + _ = x " + op + @" x; } } "; @@ -9567,9 +10279,9 @@ static void M02(T x, int y) where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (6,9): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // x <<= y; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "x " + op + "= y").WithArguments("static abstract members in interfaces").WithLocation(6, 9) + // (6,13): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // _ = x == x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "x " + op + " x").WithArguments("static abstract members in interfaces").WithLocation(6, 13) ); var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, @@ -9577,9 +10289,12 @@ static void M02(T x, int y) where T : I1 targetFramework: TargetFramework.NetCoreApp); compilation3.VerifyDiagnostics( - // (12,31): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. - // abstract static T operator<< (T x, int y); - Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, op).WithArguments("abstract", "9.0", "preview").WithLocation(12, 31) + // (12,35): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static bool operator == (T x, T y); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "==").WithArguments("abstract", "9.0", "preview").WithLocation(12, 35), + // (13,35): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static bool operator != (T x, T y); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "!=").WithArguments("abstract", "9.0", "preview").WithLocation(13, 35) ); } @@ -24094,6 +24809,73 @@ static void MT2() ); } + [Theory] + [CombinatorialData] + public void ConsumeAbstractConversionOperatorForTupleEquality_01([CombinatorialValues("==", "!=")] string op) + { + var source1 = +@" +interface I1 +{ + abstract static implicit operator bool(I1 x); + + + static void M02((int, C) x) + { + _ = x " + op + @" x; + } + + void M03((int, C) y) + { + _ = y " + op + @" y; + } +} + +class Test +{ + static void MT1((int, C) a) + { + _ = a " + op + @" a; + } + + static void MT2() where T : I1 + { + _ = (System.Linq.Expressions.Expression)>>)(((int, C) b) => (b " + op + @" b).ToString()); + } +} + +#pragma warning disable CS0660 // 'C' defines operator == or operator != but does not override Object.Equals(object o) +#pragma warning disable CS0661 // 'C' defines operator == or operator != but does not override Object.GetHashCode() + +class C +{ + public static T operator == (C x, C y) => default; + public static T operator != (C x, C y) => default; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation1.VerifyDiagnostics( + // (4,39): error CS0552: 'I1.implicit operator bool(I1)': user-defined conversions to or from an interface are not allowed + // abstract static implicit operator bool(I1 x); + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "bool").WithArguments("I1.implicit operator bool(I1)").WithLocation(4, 39), + // (9,13): error CS0029: Cannot implicitly convert type 'I1' to 'bool' + // _ = x == x; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x " + op + " x").WithArguments("I1", "bool").WithLocation(9, 13), + // (14,13): error CS0029: Cannot implicitly convert type 'I1' to 'bool' + // _ = y == y; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "y " + op + " y").WithArguments("I1", "bool").WithLocation(14, 13), + // (22,13): error CS0029: Cannot implicitly convert type 'I1' to 'bool' + // _ = a == a; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "a " + op + " a").WithArguments("I1", "bool").WithLocation(22, 13), + // (27,98): error CS8382: An expression tree may not contain a tuple == or != operator + // _ = (System.Linq.Expressions.Expression)>>)(((int, C) b) => (b == b).ToString()); + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsTupleBinOp, "b " + op + " b").WithLocation(27, 98) + ); + } + [Theory] [CombinatorialData] public void ConsumeAbstractConversionOperator_03([CombinatorialValues("implicit", "explicit")] string op) @@ -24303,6 +25085,195 @@ .maxstack 1 "); } + [Theory] + [CombinatorialData] + public void ConsumeAbstractConversionOperatorForTupleEquality_03([CombinatorialValues("==", "!=")] string op) + { + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static implicit operator bool (T x); +} + +class Test +{ + static void M02((int, C) x) where T : U where U : I1 + { + _ = x " + op + @" x; + } +} + +#pragma warning disable CS0660 // 'C' defines operator == or operator != but does not override Object.Equals(object o) +#pragma warning disable CS0661 // 'C' defines operator == or operator != but does not override Object.GetHashCode() + +class C +{ + public static T operator == (C x, C y) => default; + public static T operator != (C x, C y) => default; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + if (op == "==") + { + verifier.VerifyIL("Test.M02(System.ValueTuple>)", +@" +{ + // Code size 52 (0x34) + .maxstack 2 + .locals init (System.ValueTuple> V_0, + System.ValueTuple> V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldarg.0 + IL_0004: stloc.1 + IL_0005: ldloc.0 + IL_0006: ldfld ""int System.ValueTuple>.Item1"" + IL_000b: ldloc.1 + IL_000c: ldfld ""int System.ValueTuple>.Item1"" + IL_0011: bne.un.s IL_0031 + IL_0013: ldloc.0 + IL_0014: ldfld ""C System.ValueTuple>.Item2"" + IL_0019: ldloc.1 + IL_001a: ldfld ""C System.ValueTuple>.Item2"" + IL_001f: call ""T C.op_Equality(C, C)"" + IL_0024: constrained. ""T"" + IL_002a: call ""bool I1.op_Implicit(T)"" + IL_002f: br.s IL_0032 + IL_0031: ldc.i4.0 + IL_0032: pop + IL_0033: ret +} +"); + } + else + { + verifier.VerifyIL("Test.M02(System.ValueTuple>)", +@" +{ + // Code size 52 (0x34) + .maxstack 2 + .locals init (System.ValueTuple> V_0, + System.ValueTuple> V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldarg.0 + IL_0004: stloc.1 + IL_0005: ldloc.0 + IL_0006: ldfld ""int System.ValueTuple>.Item1"" + IL_000b: ldloc.1 + IL_000c: ldfld ""int System.ValueTuple>.Item1"" + IL_0011: bne.un.s IL_0031 + IL_0013: ldloc.0 + IL_0014: ldfld ""C System.ValueTuple>.Item2"" + IL_0019: ldloc.1 + IL_001a: ldfld ""C System.ValueTuple>.Item2"" + IL_001f: call ""T C.op_Inequality(C, C)"" + IL_0024: constrained. ""T"" + IL_002a: call ""bool I1.op_Implicit(T)"" + IL_002f: br.s IL_0032 + IL_0031: ldc.i4.1 + IL_0032: pop + IL_0033: ret +} +"); + } + + compilation1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + verifier = CompileAndVerify(compilation1, verify: Verification.Skipped).VerifyDiagnostics(); + + + if (op == "==") + { + verifier.VerifyIL("Test.M02(System.ValueTuple>)", +@" +{ + // Code size 51 (0x33) + .maxstack 2 + .locals init (System.ValueTuple> V_0, + System.ValueTuple> V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldarg.0 + IL_0003: stloc.1 + IL_0004: ldloc.0 + IL_0005: ldfld ""int System.ValueTuple>.Item1"" + IL_000a: ldloc.1 + IL_000b: ldfld ""int System.ValueTuple>.Item1"" + IL_0010: bne.un.s IL_0030 + IL_0012: ldloc.0 + IL_0013: ldfld ""C System.ValueTuple>.Item2"" + IL_0018: ldloc.1 + IL_0019: ldfld ""C System.ValueTuple>.Item2"" + IL_001e: call ""T C.op_Equality(C, C)"" + IL_0023: constrained. ""T"" + IL_0029: call ""bool I1.op_Implicit(T)"" + IL_002e: br.s IL_0031 + IL_0030: ldc.i4.0 + IL_0031: pop + IL_0032: ret +} +"); + } + else + { + verifier.VerifyIL("Test.M02(System.ValueTuple>)", +@" +{ + // Code size 51 (0x33) + .maxstack 2 + .locals init (System.ValueTuple> V_0, + System.ValueTuple> V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldarg.0 + IL_0003: stloc.1 + IL_0004: ldloc.0 + IL_0005: ldfld ""int System.ValueTuple>.Item1"" + IL_000a: ldloc.1 + IL_000b: ldfld ""int System.ValueTuple>.Item1"" + IL_0010: bne.un.s IL_0030 + IL_0012: ldloc.0 + IL_0013: ldfld ""C System.ValueTuple>.Item2"" + IL_0018: ldloc.1 + IL_0019: ldfld ""C System.ValueTuple>.Item2"" + IL_001e: call ""T C.op_Inequality(C, C)"" + IL_0023: constrained. ""T"" + IL_0029: call ""bool I1.op_Implicit(T)"" + IL_002e: br.s IL_0031 + IL_0030: ldc.i4.1 + IL_0031: pop + IL_0032: ret +} +"); + } + + var tree = compilation1.SyntaxTrees.Single(); + var model = compilation1.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().First(); + + Assert.Equal("x " + op + " x", node.ToString()); + VerifyOperationTreeForNode(compilation1, model, node, +// Information about user-defined operators isn't exposed today. +@" +ITupleBinaryOperation (BinaryOperatorKind." + (op == "==" ? "Equals" : "NotEquals") + @") (OperationKind.TupleBinary, Type: System.Boolean) (Syntax: 'x " + op + @" x') + Left: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: (System.Int32, C)) (Syntax: 'x') + Right: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: (System.Int32, C)) (Syntax: 'x') +"); + } + [Theory] [CombinatorialData] public void ConsumeAbstractConversionOperator_04([CombinatorialValues("implicit", "explicit")] string op) @@ -24352,6 +25323,62 @@ static int M02(T x) where T : I1 ); } + [Theory] + [CombinatorialData] + public void ConsumeAbstractConversionOperatorForTupleEquality_04([CombinatorialValues("==", "!=")] string op) + { + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static implicit operator bool(T x); +} +"; + var source2 = +@" +class Test +{ + static void M02((int, C) x) where T : I1 + { + _ = x " + op + @" x; + } +} + +#pragma warning disable CS0660 // 'C' defines operator == or operator != but does not override Object.Equals(object o) +#pragma warning disable CS0661 // 'C' defines operator == or operator != but does not override Object.GetHashCode() + +class C +{ + public static T operator == (C x, C y) => default; + public static T operator != (C x, C y) => default; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (6,13): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // _ = x == x; + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "x " + op + " x").WithLocation(6, 13) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.DesktopLatestExtended); + + compilation3.VerifyDiagnostics( + // (21,39): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // abstract static implicit operator bool(T x); + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "bool").WithLocation(21, 39) + ); + } + [Theory] [CombinatorialData] public void ConsumeAbstractConversionOperator_06([CombinatorialValues("implicit", "explicit")] string op) @@ -24401,6 +25428,62 @@ static int M02(T x) where T : I1 ); } + [Theory] + [CombinatorialData] + public void ConsumeAbstractConversionOperatorForTupleEquality_06([CombinatorialValues("==", "!=")] string op) + { + var source1 = +@" +public interface I1 where T : I1 +{ + abstract static implicit operator bool(T x); +} +"; + var source2 = +@" +class Test +{ + static void M02((int, C) x) where T : I1 + { + _ = x " + op + @" x; + } +} + +#pragma warning disable CS0660 // 'C' defines operator == or operator != but does not override Object.Equals(object o) +#pragma warning disable CS0661 // 'C' defines operator == or operator != but does not override Object.GetHashCode() + +class C +{ + public static T operator == (C x, C y) => default; + public static T operator != (C x, C y) => default; +} +"; + var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + var compilation2 = CreateCompilation(source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp, + references: new[] { compilation1.ToMetadataReference() }); + + compilation2.VerifyDiagnostics( + // (6,13): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // _ = x == x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "x " + op + " x").WithArguments("static abstract members in interfaces").WithLocation(6, 13) + ); + + var compilation3 = CreateCompilation(source2 + source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular9, + targetFramework: TargetFramework.NetCoreApp); + + compilation3.VerifyDiagnostics( + // (21,39): error CS8703: The modifier 'abstract' is not valid for this item in C# 9.0. Please use language version 'preview' or greater. + // abstract static implicit operator bool(T x); + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "bool").WithArguments("abstract", "9.0", "preview").WithLocation(21, 39) + ); + } + [Theory] [CombinatorialData] public void ConsumeAbstractConversionOperator_07([CombinatorialValues("implicit", "explicit")] string op) From 9897ce8232c282d47a3bba4d43582e89f221fc11 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Tue, 8 Jun 2021 15:41:41 -0700 Subject: [PATCH 096/127] Make view model side change --- .../InheritanceMarginServiceHelpers.cs | 176 +- .../InheritanceMarginTests.cs | 3438 ++++++++--------- .../InheritanceRelationship.cs | 48 +- .../InheritanceMarginHelpers.cs | 112 +- .../MarginGlyph/MemberMenuItemViewModel.cs | 13 - .../Core/Def/ServicesVSResources.resx | 12 + .../Core/Def/xlf/ServicesVSResources.cs.xlf | 20 + .../Core/Def/xlf/ServicesVSResources.de.xlf | 20 + .../Core/Def/xlf/ServicesVSResources.es.xlf | 20 + .../Core/Def/xlf/ServicesVSResources.fr.xlf | 20 + .../Core/Def/xlf/ServicesVSResources.it.xlf | 20 + .../Core/Def/xlf/ServicesVSResources.ja.xlf | 20 + .../Core/Def/xlf/ServicesVSResources.ko.xlf | 20 + .../Core/Def/xlf/ServicesVSResources.pl.xlf | 20 + .../Def/xlf/ServicesVSResources.pt-BR.xlf | 20 + .../Core/Def/xlf/ServicesVSResources.ru.xlf | 20 + .../Core/Def/xlf/ServicesVSResources.tr.xlf | 20 + .../Def/xlf/ServicesVSResources.zh-Hans.xlf | 20 + .../Def/xlf/ServicesVSResources.zh-Hant.xlf | 20 + 19 files changed, 2138 insertions(+), 1921 deletions(-) diff --git a/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs b/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs index 6a0517a9c7eb0..69c33d14e84f1 100644 --- a/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs +++ b/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs @@ -394,40 +394,40 @@ private static async ValueTask CreateInherita .Concat(overridingMemberItems)); } #endregion - private static async ValueTask CreateInheritanceMemberItemForNamedTypeAsync( - Solution solution, - INamedTypeSymbol memberSymbol, - int lineNumber, - ImmutableArray baseSymbols, - ImmutableArray derivedTypesSymbols, - CancellationToken cancellationToken) - { - var baseSymbolItems = await baseSymbols - .SelectAsArray(symbol => symbol.OriginalDefinition) - .Distinct() - .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( - solution, - symbol, - InheritanceRelationship.Implementing, - cancellationToken), cancellationToken) - .ConfigureAwait(false); - - var derivedTypeItems = await derivedTypesSymbols - .SelectAsArray(symbol => symbol.OriginalDefinition) - .Distinct() - .SelectAsArrayAsync((symbol, _) => - CreateInheritanceItemAsync(solution, - symbol, - InheritanceRelationship.Implemented, - cancellationToken), cancellationToken) - .ConfigureAwait(false); - - return new SerializableInheritanceMarginItem( - lineNumber, - FindUsagesHelpers.GetDisplayParts(memberSymbol), - memberSymbol.GetGlyph(), - baseSymbolItems.Concat(derivedTypeItems)); - } + //private static async ValueTask CreateInheritanceMemberItemForNamedTypeAsync( + // Solution solution, + // INamedTypeSymbol memberSymbol, + // int lineNumber, + // ImmutableArray baseSymbols, + // ImmutableArray derivedTypesSymbols, + // CancellationToken cancellationToken) + //{ + // var baseSymbolItems = await baseSymbols + // .SelectAsArray(symbol => symbol.OriginalDefinition) + // .Distinct() + // .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( + // solution, + // symbol, + // InheritanceRelationship.Implementing, + // cancellationToken), cancellationToken) + // .ConfigureAwait(false); + + // var derivedTypeItems = await derivedTypesSymbols + // .SelectAsArray(symbol => symbol.OriginalDefinition) + // .Distinct() + // .SelectAsArrayAsync((symbol, _) => + // CreateInheritanceItemAsync(solution, + // symbol, + // InheritanceRelationship.Implemented, + // cancellationToken), cancellationToken) + // .ConfigureAwait(false); + + // return new SerializableInheritanceMarginItem( + // lineNumber, + // FindUsagesHelpers.GetDisplayParts(memberSymbol), + // memberSymbol.GetGlyph(), + // baseSymbolItems.Concat(derivedTypeItems)); + //} private static async ValueTask CreateInheritanceItemAsync( Solution solution, @@ -454,60 +454,60 @@ private static async ValueTask CreateInherita displayName); } - private static async ValueTask CreateInheritanceMemberInfoForMemberAsync( - Solution solution, - ISymbol memberSymbol, - int lineNumber, - ImmutableArray implementingMembers, - ImmutableArray implementedMembers, - ImmutableArray overridenMembers, - ImmutableArray overridingMembers, - CancellationToken cancellationToken) - { - var implementingMemberItems = await implementingMembers - .SelectAsArray(symbol => symbol.OriginalDefinition) - .Distinct() - .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( - solution, - symbol, - InheritanceRelationship.ImplmentedMember, - cancellationToken), cancellationToken).ConfigureAwait(false); - - var implementedMemberItems = await implementedMembers - .SelectAsArray(symbol => symbol.OriginalDefinition) - .Distinct() - .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( - solution, - symbol, - InheritanceRelationship.Implemented, - cancellationToken), cancellationToken).ConfigureAwait(false); - - var overridenMemberItems = await overridenMembers - .SelectAsArray(symbol => symbol.OriginalDefinition) - .Distinct() - .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( - solution, - symbol, - InheritanceRelationship.OverriddenMember, - cancellationToken), cancellationToken).ConfigureAwait(false); - - var overridingMemberItems = await overridingMembers - .SelectAsArray(symbol => symbol.OriginalDefinition) - .Distinct() - .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( - solution, - symbol, - InheritanceRelationship.OverridingMember, - cancellationToken), cancellationToken).ConfigureAwait(false); - - return new SerializableInheritanceMarginItem( - lineNumber, - FindUsagesHelpers.GetDisplayParts(memberSymbol), - memberSymbol.GetGlyph(), - implementingMemberItems.Concat(implementedMemberItems) - .Concat(overridenMemberItems) - .Concat(overridingMemberItems)); - } + //private static async ValueTask CreateInheritanceMemberInfoForMemberAsync( + // Solution solution, + // ISymbol memberSymbol, + // int lineNumber, + // ImmutableArray implementingMembers, + // ImmutableArray implementedMembers, + // ImmutableArray overridenMembers, + // ImmutableArray overridingMembers, + // CancellationToken cancellationToken) + //{ + // var implementingMemberItems = await implementingMembers + // .SelectAsArray(symbol => symbol.OriginalDefinition) + // .Distinct() + // .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( + // solution, + // symbol, + // InheritanceRelationship.ImplmentedMember, + // cancellationToken), cancellationToken).ConfigureAwait(false); + + // var implementedMemberItems = await implementedMembers + // .SelectAsArray(symbol => symbol.OriginalDefinition) + // .Distinct() + // .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( + // solution, + // symbol, + // InheritanceRelationship.Implemented, + // cancellationToken), cancellationToken).ConfigureAwait(false); + + // var overridenMemberItems = await overridenMembers + // .SelectAsArray(symbol => symbol.OriginalDefinition) + // .Distinct() + // .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( + // solution, + // symbol, + // InheritanceRelationship.OverriddenMember, + // cancellationToken), cancellationToken).ConfigureAwait(false); + + // var overridingMemberItems = await overridingMembers + // .SelectAsArray(symbol => symbol.OriginalDefinition) + // .Distinct() + // .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( + // solution, + // symbol, + // InheritanceRelationship.OverridingMember, + // cancellationToken), cancellationToken).ConfigureAwait(false); + + // return new SerializableInheritanceMarginItem( + // lineNumber, + // FindUsagesHelpers.GetDisplayParts(memberSymbol), + // memberSymbol.GetGlyph(), + // implementingMemberItems.Concat(implementedMemberItems) + // .Concat(overridenMemberItems) + // .Concat(overridingMemberItems)); + //} #region FindAllReferenceHelpers private static ImmutableArray GetImplementingSymbolsForTypeMember( diff --git a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs index 7f1c087caf2ba..edfeabd6a9ece 100644 --- a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs +++ b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs @@ -2,1722 +2,1722 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; -using System.Linq; -using System.Security; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; -using Microsoft.CodeAnalysis.InheritanceMargin; -using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Test.Utilities; -using Roslyn.Utilities; -using Xunit; - -namespace Microsoft.CodeAnalysis.Editor.UnitTests.InheritanceMargin -{ - [Trait(Traits.Feature, Traits.Features.InheritanceMargin)] - [UseExportProvider] - public class InheritanceMarginTests - { - private const string SearchAreaTag = "SeachTag"; - - #region Helpers - - private static Task VerifyNoItemForDocumentAsync(string markup, string languageName) - => VerifyInSingleDocumentAsync(markup, languageName); - - private static Task VerifyInSingleDocumentAsync( - string markup, - string languageName, - params TestInheritanceMemberItem[] memberItems) - { - var workspaceFile = $@" - - - - {markup} - - -"; - - var cancellationToken = CancellationToken.None; - - using var testWorkspace = TestWorkspace.Create( - workspaceFile, - composition: EditorTestCompositions.EditorFeatures); - - var testHostDocument = testWorkspace.Documents[0]; - return VerifyTestMemberInDocumentAsync(testWorkspace, testHostDocument, memberItems, cancellationToken); - } - - private static async Task VerifyTestMemberInDocumentAsync( - TestWorkspace testWorkspace, - TestHostDocument testHostDocument, - TestInheritanceMemberItem[] memberItems, - CancellationToken cancellationToken) - { - var document = testWorkspace.CurrentSolution.GetRequiredDocument(testHostDocument.Id); - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var searchingSpan = root.Span; - // Look for the search span, if not found, then pass the whole document span to the service. - if (testHostDocument.AnnotatedSpans.TryGetValue(SearchAreaTag, out var spans) && spans.IsSingle()) - { - searchingSpan = spans[0]; - } - - var service = document.GetRequiredLanguageService(); - var actualItems = await service.GetInheritanceMemberItemsAsync( - document, - searchingSpan, - cancellationToken).ConfigureAwait(false); - - var sortedActualItems = actualItems.OrderBy(item => item.LineNumber).ToImmutableArray(); - var sortedExpectedItems = memberItems.OrderBy(item => item.LineNumber).ToImmutableArray(); - Assert.Equal(sortedExpectedItems.Length, sortedActualItems.Length); - - for (var i = 0; i < sortedActualItems.Length; i++) - { - VerifyInheritanceMember(testWorkspace, sortedExpectedItems[i], sortedActualItems[i]); - } - } - - private static void VerifyInheritanceMember(TestWorkspace testWorkspace, TestInheritanceMemberItem expectedItem, InheritanceMarginItem actualItem) - { - Assert.Equal(expectedItem.LineNumber, actualItem.LineNumber); - Assert.Equal(expectedItem.MemberName, actualItem.DisplayTexts.JoinText()); - Assert.Equal(expectedItem.Targets.Length, actualItem.TargetItems.Length); - var expectedTargets = expectedItem.Targets - .Select(info => TestInheritanceTargetItem.Create(info, testWorkspace)) - .OrderBy(target => target.TargetSymbolName) - .ToImmutableArray(); - var sortedActualTargets = actualItem.TargetItems.OrderBy(target => target.DefinitionItem.DisplayParts.JoinText()) - .ToImmutableArray(); - for (var i = 0; i < expectedTargets.Length; i++) - { - VerifyInheritanceTarget(expectedTargets[i], sortedActualTargets[i]); - } - } - - private static void VerifyInheritanceTarget(TestInheritanceTargetItem expectedTarget, InheritanceTargetItem actualTarget) - { - Assert.Equal(expectedTarget.TargetSymbolName, actualTarget.DefinitionItem.DisplayParts.JoinText()); - Assert.Equal(expectedTarget.RelationshipToMember, actualTarget.RelationToMember); - - if (expectedTarget.IsInMetadata) - { - Assert.True(actualTarget.DefinitionItem.Properties.ContainsKey("MetadataSymbolKey")); - Assert.True(actualTarget.DefinitionItem.SourceSpans.IsEmpty); - } - else - { - var actualDocumentSpans = actualTarget.DefinitionItem.SourceSpans.OrderBy(documentSpan => documentSpan.SourceSpan.Start).ToImmutableArray(); - var expectedDocumentSpans = expectedTarget.DocumentSpans.OrderBy(documentSpan => documentSpan.SourceSpan.Start).ToImmutableArray(); - Assert.Equal(expectedDocumentSpans.Length, actualDocumentSpans.Length); - for (var i = 0; i < actualDocumentSpans.Length; i++) - { - Assert.Equal(expectedDocumentSpans[i].SourceSpan, actualDocumentSpans[i].SourceSpan); - Assert.Equal(expectedDocumentSpans[i].Document.FilePath, actualDocumentSpans[i].Document.FilePath); - } - } - } - - /// - /// Project of markup1 is referencing project of markup2 - /// - private static async Task VerifyInDifferentProjectsAsync( - (string markupInProject1, string languageName) markup1, - (string markupInProject2, string languageName) markup2, - TestInheritanceMemberItem[] memberItemsInMarkup1, - TestInheritanceMemberItem[] memberItemsInMarkup2) - { - var workspaceFile = - $@" - - - Assembly2 - - {markup1.markupInProject1} - - - - - {markup2.markupInProject2} - - -"; - - var cancellationToken = CancellationToken.None; - using var testWorkspace = TestWorkspace.Create( - workspaceFile, - composition: EditorTestCompositions.EditorFeatures); - - var testHostDocument1 = testWorkspace.Documents.Single(doc => doc.Project.AssemblyName.Equals("Assembly1")); - var testHostDocument2 = testWorkspace.Documents.Single(doc => doc.Project.AssemblyName.Equals("Assembly2")); - await VerifyTestMemberInDocumentAsync(testWorkspace, testHostDocument1, memberItemsInMarkup1, cancellationToken).ConfigureAwait(false); - await VerifyTestMemberInDocumentAsync(testWorkspace, testHostDocument2, memberItemsInMarkup2, cancellationToken).ConfigureAwait(false); - } - - private class TestInheritanceMemberItem - { - public readonly int LineNumber; - public readonly string MemberName; - public readonly ImmutableArray Targets; - - public TestInheritanceMemberItem( - int lineNumber, - string memberName, - ImmutableArray targets) - { - LineNumber = lineNumber; - MemberName = memberName; - Targets = targets; - } - } - - private class TargetInfo - { - public readonly string TargetSymbolDisplayName; - public readonly string? LocationTag; - public readonly InheritanceRelationship Relationship; - public readonly bool InMetadata; - - public TargetInfo( - string targetSymbolDisplayName, - string locationTag, - InheritanceRelationship relationship) - { - TargetSymbolDisplayName = targetSymbolDisplayName; - LocationTag = locationTag; - Relationship = relationship; - InMetadata = false; - } - - public TargetInfo( - string targetSymbolDisplayName, - InheritanceRelationship relationship, - bool inMetadata) - { - TargetSymbolDisplayName = targetSymbolDisplayName; - Relationship = relationship; - InMetadata = inMetadata; - LocationTag = null; - } - } - - private class TestInheritanceTargetItem - { - public readonly string TargetSymbolName; - public readonly InheritanceRelationship RelationshipToMember; - public readonly ImmutableArray DocumentSpans; - public readonly bool IsInMetadata; - - public TestInheritanceTargetItem( - string targetSymbolName, - InheritanceRelationship relationshipToMember, - ImmutableArray documentSpans, - bool isInMetadata) - { - TargetSymbolName = targetSymbolName; - RelationshipToMember = relationshipToMember; - DocumentSpans = documentSpans; - IsInMetadata = isInMetadata; - } - - public static TestInheritanceTargetItem Create( - TargetInfo targetInfo, - TestWorkspace testWorkspace) - { - if (targetInfo.InMetadata) - { - return new TestInheritanceTargetItem( - targetInfo.TargetSymbolDisplayName, - targetInfo.Relationship, - ImmutableArray.Empty, - isInMetadata: true); - } - else - { - using var _ = ArrayBuilder.GetInstance(out var builder); - // If the target is not in metadata, there must be a location tag to give the span! - Assert.True(targetInfo.LocationTag != null); - foreach (var testHostDocument in testWorkspace.Documents) - { - if (targetInfo.LocationTag != null) - { - var annotatedSpans = testHostDocument.AnnotatedSpans; - if (annotatedSpans.TryGetValue(targetInfo.LocationTag, out var spans)) - { - var document = testWorkspace.CurrentSolution.GetRequiredDocument(testHostDocument.Id); - builder.AddRange(spans.Select(span => new DocumentSpan(document, span))); - } - } - } - - return new TestInheritanceTargetItem( - targetInfo.TargetSymbolDisplayName, - targetInfo.Relationship, - builder.ToImmutable(), - isInMetadata: false); - } - } - } - - #endregion - - #region TestsForCSharp - - [Fact] - public Task TestCSharpClassWithErrorBaseType() - { - var markup = @" -public class Bar : SomethingUnknown -{ -}"; - return VerifyNoItemForDocumentAsync(markup, LanguageNames.CSharp); - } - - [Fact] - public Task TestCSharpReferencingMetadata() - { - var markup = @" -using System.Collections; -public class Bar : IEnumerable -{ - public IEnumerator GetEnumerator () { return null }; -}"; - var itemForBar = new TestInheritanceMemberItem( - lineNumber: 3, - memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "interface IEnumerable", - relationship: InheritanceRelationship.Implementing, - inMetadata: true))); - - var itemForGetEnumerator = new TestInheritanceMemberItem( - lineNumber: 5, - memberName: "IEnumerator Bar.GetEnumerator()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "IEnumerator IEnumerable.GetEnumerator()", - relationship: InheritanceRelationship.Implementing, - inMetadata: true))); - - return VerifyInSingleDocumentAsync(markup, LanguageNames.CSharp, itemForBar, itemForGetEnumerator); - } - - [Fact] - public Task TestCSharpClassImplementingInterface() - { - var markup = @" -interface {|target1:IBar|} { } -public class {|target2:Bar|} : IBar -{ -} - "; - - var itemOnLine2 = new TestInheritanceMemberItem( - lineNumber: 2, - memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "class Bar", - locationTag: "target2", - relationship: InheritanceRelationship.Implemented))); - - var itemOnLine3 = new TestInheritanceMemberItem( - lineNumber: 3, - memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "interface IBar", - locationTag: "target1", - relationship: InheritanceRelationship.Implementing))); - - return VerifyInSingleDocumentAsync( - markup, - LanguageNames.CSharp, - itemOnLine2, - itemOnLine3); - } - - [Fact] - public Task TestCSharpInterfaceImplementingInterface() - { - var markup = @" -interface {|target1:IBar|} { } -interface {|target2:IBar2|} : IBar { } - "; - - var itemOnLine2 = new TestInheritanceMemberItem( - lineNumber: 2, - memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "interface IBar2", - locationTag: "target2", - relationship: InheritanceRelationship.Implemented)) - ); - var itemOnLine3 = new TestInheritanceMemberItem( - lineNumber: 3, - memberName: "interface IBar2", - targets: ImmutableArray.Empty - .Add(new TargetInfo( - targetSymbolDisplayName: "interface IBar", - locationTag: "target1", - relationship: InheritanceRelationship.Implementing)) - ); - - return VerifyInSingleDocumentAsync( - markup, - LanguageNames.CSharp, - itemOnLine2, - itemOnLine3); - } - - [Fact] - public Task TestCSharpClassInheritsClass() - { - var markup = @" -class {|target2:A|} { } -class {|target1:B|} : A { } - "; - - var itemOnLine2 = new TestInheritanceMemberItem( - lineNumber: 2, - memberName: "class A", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "class B", - locationTag: "target1", - relationship: InheritanceRelationship.Implemented)) - ); - var itemOnLine3 = new TestInheritanceMemberItem( - lineNumber: 3, - memberName: "class B", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "class A", - locationTag: "target2", - relationship: InheritanceRelationship.Implementing)) - ); - - return VerifyInSingleDocumentAsync( - markup, - LanguageNames.CSharp, - itemOnLine2, - itemOnLine3); - } - - [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("enum")] - [InlineData("interface")] - public Task TestCSharpTypeWithoutBaseType(string typeName) - { - var markup = $@" -public {typeName} Bar -{{ -}}"; - return VerifyNoItemForDocumentAsync(markup, LanguageNames.CSharp); - } - - [Theory] - [InlineData("public Bar() { }")] - [InlineData("public static void Bar3() { }")] - [InlineData("public static void ~Bar() { }")] - [InlineData("public static Bar operator +(Bar a, Bar b) => new Bar();")] - public Task TestCSharpSpecialMember(string memberDeclaration) - { - var markup = $@" -public abstract class {{|target1:Bar1|}} -{{}} -public class Bar : Bar1 -{{ - {{|{SearchAreaTag}:{memberDeclaration}|}} -}}"; - return VerifyInSingleDocumentAsync( - markup, - LanguageNames.CSharp, - new TestInheritanceMemberItem( - lineNumber: 4, - memberName: "class Bar", - targets: ImmutableArray.Create( - new TargetInfo( - targetSymbolDisplayName: "class Bar1", - locationTag: "target1", - relationship: InheritanceRelationship.Implementing)))); - } - - [Fact] - public Task TestCSharpMetadataInterface() - { - var markup = @" -using System.Collections; -public class Bar : IEnumerable -{ -}"; - return VerifyInSingleDocumentAsync( - markup, - LanguageNames.CSharp, - new TestInheritanceMemberItem( - lineNumber: 3, - memberName: "class Bar", - targets: ImmutableArray.Create( - new TargetInfo( - targetSymbolDisplayName: "interface IEnumerable", - relationship: InheritanceRelationship.Implementing, - inMetadata: true)))); - } - - [Fact] - public Task TestCSharpEventDeclaration() - { - var markup = @" -using System; -interface {|target2:IBar|} -{ - event EventHandler {|target4:e|}; -} -public class {|target1:Bar|} : IBar -{ - public event EventHandler {|target3:e|} - { - add {} remove {} - } -}"; - var itemForIBar = new TestInheritanceMemberItem( - lineNumber: 3, - memberName: "interface IBar", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "class Bar", - locationTag: "target1", - relationship: InheritanceRelationship.Implemented))); - - var itemForBar = new TestInheritanceMemberItem( - lineNumber: 7, - memberName: "class Bar", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "interface IBar", - locationTag: "target2", - relationship: InheritanceRelationship.Implementing))); - - var itemForEventInInterface = new TestInheritanceMemberItem( - lineNumber: 5, - memberName: "event EventHandler IBar.e", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "event EventHandler Bar.e", - locationTag: "target3", - relationship: InheritanceRelationship.Implemented))); - - var itemForEventInClass = new TestInheritanceMemberItem( - lineNumber: 9, - memberName: "event EventHandler Bar.e", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "event EventHandler IBar.e", - locationTag: "target4", - relationship: InheritanceRelationship.Implementing))); - - return VerifyInSingleDocumentAsync( - markup, - LanguageNames.CSharp, - itemForIBar, - itemForBar, - itemForEventInInterface, - itemForEventInClass); - } - - [Fact] - public Task TestCSharpEventFieldDeclarations() - { - var markup = @"using System; -interface {|target2:IBar|} -{ - event EventHandler {|target5:e1|}, {|target6:e2|}; -} -public class {|target1:Bar|} : IBar -{ - public event EventHandler {|target3:e1|}, {|target4:e2|}; -}"; - var itemForIBar = new TestInheritanceMemberItem( - lineNumber: 2, - memberName: "interface IBar", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "class Bar", - locationTag: "target1", - relationship: InheritanceRelationship.Implemented))); - - var itemForBar = new TestInheritanceMemberItem( - lineNumber: 6, - memberName: "class Bar", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "interface IBar", - locationTag: "target2", - relationship: InheritanceRelationship.Implementing))); - - var itemForE1InInterface = new TestInheritanceMemberItem( - lineNumber: 4, - memberName: "event EventHandler IBar.e1", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "event EventHandler Bar.e1", - locationTag: "target3", - relationship: InheritanceRelationship.Implemented))); - - var itemForE2InInterface = new TestInheritanceMemberItem( - lineNumber: 4, - memberName: "event EventHandler IBar.e2", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "event EventHandler Bar.e2", - locationTag: "target4", - relationship: InheritanceRelationship.Implemented))); - - var itemForE1InClass = new TestInheritanceMemberItem( - lineNumber: 8, - memberName: "event EventHandler Bar.e1", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "event EventHandler IBar.e1", - locationTag: "target5", - relationship: InheritanceRelationship.Implementing))); - - var itemForE2InClass = new TestInheritanceMemberItem( - lineNumber: 8, - memberName: "event EventHandler Bar.e2", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "event EventHandler IBar.e2", - locationTag: "target6", - relationship: InheritanceRelationship.Implementing))); - - return VerifyInSingleDocumentAsync( - markup, - LanguageNames.CSharp, - itemForIBar, - itemForBar, - itemForE1InInterface, - itemForE2InInterface, - itemForE1InClass, - itemForE2InClass); - } - - [Fact] - public Task TestCSharpInterfaceMembers() - { - var markup = @"using System; -interface {|target1:IBar|} -{ - void {|target4:Foo|}(); - int {|target6:Poo|} { get; set; } - event EventHandler {|target8:Eoo|}; - int {|target9:this|}[int i] { get; set; } -} -public class {|target2:Bar|} : IBar -{ - public void {|target3:Foo|}() { } - public int {|target5:Poo|} { get; set; } - public event EventHandler {|target7:Eoo|}; - public int {|target10:this|}[int i] { get => 1; set { } } -}"; - var itemForEooInClass = new TestInheritanceMemberItem( - lineNumber: 13, - memberName: "event EventHandler Bar.Eoo", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "event EventHandler IBar.Eoo", - locationTag: "target8", - relationship: InheritanceRelationship.Implementing)) - ); - - var itemForEooInInterface = new TestInheritanceMemberItem( - lineNumber: 6, - memberName: "event EventHandler IBar.Eoo", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "event EventHandler Bar.Eoo", - locationTag: "target7", - relationship: InheritanceRelationship.Implemented)) - ); - - var itemForPooInInterface = new TestInheritanceMemberItem( - lineNumber: 5, - memberName: "int IBar.Poo { get; set; }", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "int Bar.Poo { get; set; }", - locationTag: "target5", - relationship: InheritanceRelationship.Implemented)) - ); - - var itemForPooInClass = new TestInheritanceMemberItem( - lineNumber: 12, - memberName: "int Bar.Poo { get; set; }", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "int IBar.Poo { get; set; }", - locationTag: "target6", - relationship: InheritanceRelationship.Implementing)) - ); - - var itemForFooInInterface = new TestInheritanceMemberItem( - lineNumber: 4, - memberName: "void IBar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "void Bar.Foo()", - locationTag: "target3", - relationship: InheritanceRelationship.Implemented)) - ); - - var itemForFooInClass = new TestInheritanceMemberItem( - lineNumber: 11, - memberName: "void Bar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "void IBar.Foo()", - locationTag: "target4", - relationship: InheritanceRelationship.Implementing)) - ); - - var itemForIBar = new TestInheritanceMemberItem( - lineNumber: 2, - memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "class Bar", - locationTag: "target2", - relationship: InheritanceRelationship.Implemented)) - ); - - var itemForBar = new TestInheritanceMemberItem( - lineNumber: 9, - memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "interface IBar", - locationTag: "target1", - relationship: InheritanceRelationship.Implementing)) - ); - - var itemForIndexerInClass = new TestInheritanceMemberItem( - lineNumber: 14, - memberName: "int Bar.this[int] { get; set; }", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "int IBar.this[int] { get; set; }", - locationTag: "target9", - relationship: InheritanceRelationship.Implementing)) - ); - - var itemForIndexerInInterface = new TestInheritanceMemberItem( - lineNumber: 7, - memberName: "int IBar.this[int] { get; set; }", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "int Bar.this[int] { get; set; }", - locationTag: "target10", - relationship: InheritanceRelationship.Implemented)) - ); - - return VerifyInSingleDocumentAsync( - markup, - LanguageNames.CSharp, - itemForEooInClass, - itemForEooInInterface, - itemForPooInInterface, - itemForPooInClass, - itemForFooInInterface, - itemForFooInClass, - itemForIBar, - itemForBar, - itemForIndexerInInterface, - itemForIndexerInClass); - } - - [Theory] - [InlineData("abstract")] - [InlineData("virtual")] - public Task TestCSharpAbstractClassMembers(string modifier) - { - var markup = $@"using System; -public abstract class {{|target2:Bar|}} -{{ - public {modifier} void {{|target4:Foo|}}(); - public {modifier} int {{|target6:Poo|}} {{ get; set; }} - public {modifier} event EventHandler {{|target8:Eoo|}}; -}} -public class {{|target1:Bar2|}} : Bar -{{ - public override void {{|target3:Foo|}}() {{ }} - public override int {{|target5:Poo|}} {{ get; set; }} - public override event EventHandler {{|target7:Eoo|}}; -}} - "; - - var itemForEooInClass = new TestInheritanceMemberItem( - lineNumber: 12, - memberName: "override event EventHandler Bar2.Eoo", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: $"{modifier} event EventHandler Bar.Eoo", - locationTag: "target8", - relationship: InheritanceRelationship.Overriding))); - - var itemForEooInAbstractClass = new TestInheritanceMemberItem( - lineNumber: 6, - memberName: $"{modifier} event EventHandler Bar.Eoo", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "override event EventHandler Bar2.Eoo", - locationTag: "target7", - relationship: InheritanceRelationship.Overridden))); - - var itemForPooInClass = new TestInheritanceMemberItem( - lineNumber: 11, - memberName: "override int Bar2.Poo { get; set; }", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: $"{modifier} int Bar.Poo {{ get; set; }}", - locationTag: "target6", - relationship: InheritanceRelationship.Overriding))); - - var itemForPooInAbstractClass = new TestInheritanceMemberItem( - lineNumber: 5, - memberName: $"{modifier} int Bar.Poo {{ get; set; }}", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "override int Bar2.Poo { get; set; }", - locationTag: "target5", - relationship: InheritanceRelationship.Overridden))); - - var itemForFooInAbstractClass = new TestInheritanceMemberItem( - lineNumber: 4, - memberName: $"{modifier} void Bar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "override void Bar2.Foo()", - locationTag: "target3", - relationship: InheritanceRelationship.Overridden))); - - var itemForFooInClass = new TestInheritanceMemberItem( - lineNumber: 10, - memberName: "override void Bar2.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: $"{modifier} void Bar.Foo()", - locationTag: "target4", - relationship: InheritanceRelationship.Overriding))); - - var itemForBar = new TestInheritanceMemberItem( - lineNumber: 2, - memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "class Bar2", - locationTag: "target1", - relationship: InheritanceRelationship.Implemented))); - - var itemForBar2 = new TestInheritanceMemberItem( - lineNumber: 8, - memberName: "class Bar2", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "class Bar", - locationTag: "target2", - relationship: InheritanceRelationship.Implementing))); - - return VerifyInSingleDocumentAsync( - markup, - LanguageNames.CSharp, - itemForBar, - itemForBar2, - itemForFooInAbstractClass, - itemForFooInClass, - itemForPooInClass, - itemForPooInAbstractClass, - itemForEooInClass, - itemForEooInAbstractClass); - } - - [Theory] - [CombinatorialData] - public Task TestCSharpOverrideMemberCanFindImplementingInterface(bool testDuplicate) - { - var markup1 = @"using System; -public interface {|target4:IBar|} -{ - void {|target6:Foo|}(); -} -public class {|target1:Bar1|} : IBar -{ - public virtual void {|target2:Foo|}() { } -} -public class {|target5:Bar2|} : Bar1 -{ - public override void {|target3:Foo|}() { } -}"; - - var markup2 = @"using System; -public interface {|target4:IBar|} -{ - void {|target6:Foo|}(); -} -public class {|target1:Bar1|} : IBar -{ - public virtual void {|target2:Foo|}() { } -} -public class {|target5:Bar2|} : Bar1, IBar -{ - public override void {|target3:Foo|}() { } -}"; - - var itemForIBar = new TestInheritanceMemberItem( - lineNumber: 2, - memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "class Bar1", - locationTag: "target1", - relationship: InheritanceRelationship.Implemented), - new TargetInfo( - targetSymbolDisplayName: "class Bar2", - locationTag: "target5", - relationship: InheritanceRelationship.Implemented))); - - var itemForFooInIBar = new TestInheritanceMemberItem( - lineNumber: 4, - memberName: "void IBar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "virtual void Bar1.Foo()", - locationTag: "target2", - relationship: InheritanceRelationship.Implemented), - new TargetInfo( - targetSymbolDisplayName: "override void Bar2.Foo()", - locationTag: "target3", - relationship: InheritanceRelationship.Implemented))); - - var itemForBar1 = new TestInheritanceMemberItem( - lineNumber: 6, - memberName: "class Bar1", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "interface IBar", - locationTag: "target4", - relationship: InheritanceRelationship.Implementing), - new TargetInfo( - targetSymbolDisplayName: "class Bar2", - locationTag: "target5", - relationship: InheritanceRelationship.Implemented))); - - var itemForFooInBar1 = new TestInheritanceMemberItem( - lineNumber: 8, - memberName: "virtual void Bar1.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "void IBar.Foo()", - locationTag: "target6", - relationship: InheritanceRelationship.Implementing), - new TargetInfo( - targetSymbolDisplayName: "override void Bar2.Foo()", - locationTag: "target3", - relationship: InheritanceRelationship.Overridden))); - - var itemForBar2 = new TestInheritanceMemberItem( - lineNumber: 10, - memberName: "class Bar2", - targets: ImmutableArray.Create( - new TargetInfo( - targetSymbolDisplayName: "class Bar1", - locationTag: "target1", - relationship: InheritanceRelationship.Implementing), - new TargetInfo( - targetSymbolDisplayName: "interface IBar", - locationTag: "target4", - relationship: InheritanceRelationship.Implementing))); - - var itemForFooInBar2 = new TestInheritanceMemberItem( - lineNumber: 12, - memberName: "override void Bar2.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "void IBar.Foo()", - locationTag: "target6", - relationship: InheritanceRelationship.Implementing), - new TargetInfo( - targetSymbolDisplayName: "virtual void Bar1.Foo()", - locationTag: "target2", - relationship: InheritanceRelationship.Overriding))); - - return VerifyInSingleDocumentAsync( - testDuplicate ? markup2 : markup1, - LanguageNames.CSharp, - itemForIBar, - itemForFooInIBar, - itemForBar1, - itemForFooInBar1, - itemForBar2, - itemForFooInBar2); - } - - [Fact] - public Task TestCSharpFindGenericsBaseType() - { - var lessThanToken = SecurityElement.Escape("<"); - var greaterThanToken = SecurityElement.Escape(">"); - var markup = $@" -public interface {{|target2:IBar|}}{lessThanToken}T{greaterThanToken} -{{ - void {{|target4:Foo|}}(); -}} - -public class {{|target1:Bar2|}} : IBar{lessThanToken}int{greaterThanToken}, IBar{lessThanToken}string{greaterThanToken} -{{ - public void {{|target3:Foo|}}(); -}}"; - - var itemForIBar = new TestInheritanceMemberItem( - lineNumber: 2, - memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "class Bar2", - locationTag: "target1", - relationship: InheritanceRelationship.Implemented))); - - var itemForFooInIBar = new TestInheritanceMemberItem( - lineNumber: 4, - memberName: "void IBar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "void Bar2.Foo()", - locationTag: "target3", - relationship: InheritanceRelationship.Implemented))); - - // Only have one IBar item - var itemForBar2 = new TestInheritanceMemberItem( - lineNumber: 7, - memberName: "class Bar2", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "interface IBar", - locationTag: "target2", - relationship: InheritanceRelationship.Implementing))); - - // Only have one IBar.Foo item - var itemForFooInBar2 = new TestInheritanceMemberItem( - lineNumber: 9, - memberName: "void Bar2.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "void IBar.Foo()", - locationTag: "target4", - relationship: InheritanceRelationship.Implementing))); - - return VerifyInSingleDocumentAsync( - markup, - LanguageNames.CSharp, - itemForIBar, - itemForFooInIBar, - itemForBar2, - itemForFooInBar2); - } - - #endregion - - #region TestsForVisualBasic - - [Fact] - public Task TestVisualBasicWithErrorBaseType() - { - var markup = @" -Namespace MyNamespace - Public Class Bar - Implements SomethingNotExist - End Class -End Namespace"; - - return VerifyNoItemForDocumentAsync(markup, LanguageNames.VisualBasic); - } - - [Fact] - public Task TestVisualBasicReferencingMetadata() - { - var markup = @" -Namespace MyNamespace - Public Class Bar - Implements System.Collections.IEnumerable - Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator - Throw New NotImplementedException() - End Function - End Class -End Namespace"; - var itemForBar = new TestInheritanceMemberItem( - lineNumber: 3, - memberName: "Class Bar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Interface IEnumerable", - relationship: InheritanceRelationship.Implementing, - inMetadata: true))); - - var itemForGetEnumerator = new TestInheritanceMemberItem( - lineNumber: 5, - memberName: "Function Bar.GetEnumerator() As IEnumerator", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Function IEnumerable.GetEnumerator() As IEnumerator", - relationship: InheritanceRelationship.Implementing, - inMetadata: true))); - - return VerifyInSingleDocumentAsync(markup, LanguageNames.VisualBasic, itemForBar, itemForGetEnumerator); - } - - [Fact] - public Task TestVisualBasicClassImplementingInterface() - { - var markup = @" -Interface {|target2:IBar|} -End Interface -Class {|target1:Bar|} - Implements IBar -End Class"; - var itemForIBar = new TestInheritanceMemberItem( - lineNumber: 2, - memberName: "Interface IBar", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Class Bar", - locationTag: "target1", - relationship: InheritanceRelationship.Implemented))); - - var itemForBar = new TestInheritanceMemberItem( - lineNumber: 4, - memberName: "Class Bar", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Interface IBar", - locationTag: "target2", - relationship: InheritanceRelationship.Implementing))); - - return VerifyInSingleDocumentAsync( - markup, - LanguageNames.VisualBasic, - itemForIBar, - itemForBar); - } - - [Fact] - public Task TestVisualBasicInterfaceImplementingInterface() - { - var markup = @" -Interface {|target2:IBar2|} -End Interface -Interface {|target1:IBar|} - Inherits IBar2 -End Interface"; - - var itemForIBar2 = new TestInheritanceMemberItem( - lineNumber: 2, - memberName: "Interface IBar2", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Interface IBar", - locationTag: "target1", - relationship: InheritanceRelationship.Implemented))); - - var itemForIBar = new TestInheritanceMemberItem( - lineNumber: 4, - memberName: "Interface IBar", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Interface IBar2", - locationTag: "target2", - relationship: InheritanceRelationship.Implementing))); - return VerifyInSingleDocumentAsync(markup, LanguageNames.VisualBasic, itemForIBar2, itemForIBar); - } - - [Fact] - public Task TestVisualBasicClassInheritsClass() - { - var markup = @" -Class {|target2:Bar2|} -End Class -Class {|target1:Bar|} - Inherits Bar2 -End Class"; - - var itemForBar2 = new TestInheritanceMemberItem( - lineNumber: 2, - memberName: "Class Bar2", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Class Bar", - locationTag: "target1", - relationship: InheritanceRelationship.Implemented))); - - var itemForBar = new TestInheritanceMemberItem( - lineNumber: 4, - memberName: "Class Bar", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Class Bar2", - locationTag: "target2", - relationship: InheritanceRelationship.Implementing))); - return VerifyInSingleDocumentAsync(markup, LanguageNames.VisualBasic, itemForBar2, itemForBar); - } - - [Theory] - [InlineData("Class")] - [InlineData("Structure")] - [InlineData("Enum")] - [InlineData("Interface")] - public Task TestVisualBasicTypeWithoutBaseType(string typeName) - { - var markup = $@" -{typeName} Bar -End {typeName}"; - - return VerifyNoItemForDocumentAsync(markup, LanguageNames.VisualBasic); - } - - [Fact] - public Task TestVisualBasicMetadataInterface() - { - var markup = @" -Imports System.Collections -Class Bar - Implements IEnumerable -End Class"; - return VerifyInSingleDocumentAsync( - markup, - LanguageNames.VisualBasic, - new TestInheritanceMemberItem( - lineNumber: 3, - memberName: "Class Bar", - targets: ImmutableArray.Create( - new TargetInfo( - targetSymbolDisplayName: "Interface IEnumerable", - relationship: InheritanceRelationship.Implementing, - inMetadata: true)))); - } - - [Fact] - public Task TestVisualBasicEventStatement() - { - var markup = @" -Interface {|target2:IBar|} - Event {|target4:e|} As EventHandler -End Interface -Class {|target1:Bar|} - Implements IBar - Public Event {|target3:e|} As EventHandler Implements IBar.e -End Class"; - - var itemForIBar = new TestInheritanceMemberItem( - lineNumber: 2, - memberName: "Interface IBar", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Class Bar", - locationTag: "target1", - relationship: InheritanceRelationship.Implemented))); - - var itemForBar = new TestInheritanceMemberItem( - lineNumber: 5, - memberName: "Class Bar", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Interface IBar", - locationTag: "target2", - relationship: InheritanceRelationship.Implementing))); - - var itemForEventInInterface = new TestInheritanceMemberItem( - lineNumber: 3, - memberName: "Event IBar.e As EventHandler", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Event Bar.e As EventHandler", - locationTag: "target3", - relationship: InheritanceRelationship.Implemented))); - - var itemForEventInClass = new TestInheritanceMemberItem( - lineNumber: 7, - memberName: "Event Bar.e As EventHandler", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Event IBar.e As EventHandler", - locationTag: "target4", - relationship: InheritanceRelationship.Implementing))); - - return VerifyInSingleDocumentAsync( - markup, - LanguageNames.VisualBasic, - itemForIBar, - itemForBar, - itemForEventInInterface, - itemForEventInClass); - } - - [Fact] - public Task TestVisualBasicEventBlock() - { - var markup = @" -Interface {|target2:IBar|} - Event {|target4:e|} As EventHandler -End Interface -Class {|target1:Bar|} - Implements IBar - Public Custom Event {|target3:e|} As EventHandler Implements IBar.e - End Event -End Class"; - var itemForIBar = new TestInheritanceMemberItem( - lineNumber: 2, - memberName: "Interface IBar", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Class Bar", - locationTag: "target1", - relationship: InheritanceRelationship.Implemented))); - - var itemForBar = new TestInheritanceMemberItem( - lineNumber: 5, - memberName: "Class Bar", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Interface IBar", - locationTag: "target2", - relationship: InheritanceRelationship.Implementing))); - - var itemForEventInInterface = new TestInheritanceMemberItem( - lineNumber: 3, - memberName: "Event IBar.e As EventHandler", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Event Bar.e As EventHandler", - locationTag: "target3", - relationship: InheritanceRelationship.Implemented))); - - var itemForEventInClass = new TestInheritanceMemberItem( - lineNumber: 7, - memberName: "Event Bar.e As EventHandler", - ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Event IBar.e As EventHandler", - locationTag: "target4", - relationship: InheritanceRelationship.Implementing))); - - return VerifyInSingleDocumentAsync( - markup, - LanguageNames.VisualBasic, - itemForIBar, - itemForBar, - itemForEventInInterface, - itemForEventInClass); - } - - [Fact] - public Task TestVisualBasicInterfaceMembers() - { - var markup = @" -Interface {|target2:IBar|} - Property {|target4:Poo|} As Integer - Function {|target6:Foo|}() As Integer -End Interface - -Class {|target1:Bar|} - Implements IBar - Public Property {|target3:Poo|} As Integer Implements IBar.Poo - Get - Return 1 - End Get - Set(value As Integer) - End Set - End Property - Public Function {|target5:Foo|}() As Integer Implements IBar.Foo - Return 1 - End Function -End Class"; - var itemForIBar = new TestInheritanceMemberItem( - lineNumber: 2, - memberName: "Interface IBar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Class Bar", - locationTag: "target1", - relationship: InheritanceRelationship.Implemented))); - - var itemForBar = new TestInheritanceMemberItem( - lineNumber: 7, - memberName: "Class Bar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Interface IBar", - locationTag: "target2", - relationship: InheritanceRelationship.Implementing))); - - var itemForPooInInterface = new TestInheritanceMemberItem( - lineNumber: 3, - memberName: "Property IBar.Poo As Integer", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Property Bar.Poo As Integer", - locationTag: "target3", - relationship: InheritanceRelationship.Implemented))); - - var itemForPooInClass = new TestInheritanceMemberItem( - lineNumber: 9, - memberName: "Property Bar.Poo As Integer", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Property IBar.Poo As Integer", - locationTag: "target4", - relationship: InheritanceRelationship.Implementing))); - - var itemForFooInInterface = new TestInheritanceMemberItem( - lineNumber: 4, - memberName: "Function IBar.Foo() As Integer", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Function Bar.Foo() As Integer", - locationTag: "target5", - relationship: InheritanceRelationship.Implemented))); - - var itemForFooInClass = new TestInheritanceMemberItem( - lineNumber: 16, - memberName: "Function Bar.Foo() As Integer", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Function IBar.Foo() As Integer", - locationTag: "target6", - relationship: InheritanceRelationship.Implementing))); - - return VerifyInSingleDocumentAsync( - markup, - LanguageNames.VisualBasic, - itemForIBar, - itemForBar, - itemForPooInInterface, - itemForPooInClass, - itemForFooInInterface, - itemForFooInClass); - } - - [Fact] - public Task TestVisualBasicMustInheritClassMember() - { - var markup = @" -MustInherit Class {|target2:Bar1|} - Public MustOverride Sub {|target4:Foo|}() -End Class - -Class {|target1:Bar|} - Inherits Bar1 - Public Overrides Sub {|target3:Foo|}() - End Sub -End Class"; - var itemForBar1 = new TestInheritanceMemberItem( - lineNumber: 2, - memberName: "Class Bar1", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: $"Class Bar", - locationTag: "target1", - relationship: InheritanceRelationship.Implemented))); - - var itemForBar = new TestInheritanceMemberItem( - lineNumber: 6, - memberName: "Class Bar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Class Bar1", - locationTag: "target2", - relationship: InheritanceRelationship.Implementing))); - - var itemForFooInBar1 = new TestInheritanceMemberItem( - lineNumber: 3, - memberName: "MustOverride Sub Bar1.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Overrides Sub Bar.Foo()", - locationTag: "target3", - relationship: InheritanceRelationship.Overridden))); - - var itemForFooInBar = new TestInheritanceMemberItem( - lineNumber: 8, - memberName: "Overrides Sub Bar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "MustOverride Sub Bar1.Foo()", - locationTag: "target4", - relationship: InheritanceRelationship.Overriding))); - - return VerifyInSingleDocumentAsync( - markup, - LanguageNames.VisualBasic, - itemForBar1, - itemForBar, - itemForFooInBar1, - itemForFooInBar); - } - - [Theory] - [CombinatorialData] - public Task TestVisualBasicOverrideMemberCanFindImplementingInterface(bool testDuplicate) - { - var markup1 = @" -Interface {|target4:IBar|} - Sub {|target6:Foo|}() -End Interface - -Class {|target1:Bar1|} - Implements IBar - Public Overridable Sub {|target2:Foo|}() Implements IBar.Foo - End Sub -End Class - -Class {|target5:Bar2|} - Inherits Bar1 - Public Overrides Sub {|target3:Foo|}() - End Sub -End Class"; - - var markup2 = @" -Interface {|target4:IBar|} - Sub {|target6:Foo|}() -End Interface - -Class {|target1:Bar1|} - Implements IBar - Public Overridable Sub {|target2:Foo|}() Implements IBar.Foo - End Sub -End Class - -Class {|target5:Bar2|} - Inherits Bar1 - Public Overrides Sub {|target3:Foo|}() - End Sub -End Class"; - var itemForIBar = new TestInheritanceMemberItem( - lineNumber: 2, - memberName: "Interface IBar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Class Bar1", - locationTag: "target1", - relationship: InheritanceRelationship.Implemented), - new TargetInfo( - targetSymbolDisplayName: "Class Bar2", - locationTag: "target5", - relationship: InheritanceRelationship.Implemented))); - - var itemForFooInIBar = new TestInheritanceMemberItem( - lineNumber: 3, - memberName: "Sub IBar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Overridable Sub Bar1.Foo()", - locationTag: "target2", - relationship: InheritanceRelationship.Implemented), - new TargetInfo( - targetSymbolDisplayName: "Overrides Sub Bar2.Foo()", - locationTag: "target3", - relationship: InheritanceRelationship.Implemented))); - - var itemForBar1 = new TestInheritanceMemberItem( - lineNumber: 6, - memberName: "Class Bar1", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Interface IBar", - locationTag: "target4", - relationship: InheritanceRelationship.Implementing), - new TargetInfo( - targetSymbolDisplayName: "Class Bar2", - locationTag: "target5", - relationship: InheritanceRelationship.Implemented))); - - var itemForFooInBar1 = new TestInheritanceMemberItem( - lineNumber: 8, - memberName: "Overridable Sub Bar1.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Sub IBar.Foo()", - locationTag: "target6", - relationship: InheritanceRelationship.Implementing), - new TargetInfo( - targetSymbolDisplayName: "Overrides Sub Bar2.Foo()", - locationTag: "target3", - relationship: InheritanceRelationship.Overridden))); - - var itemForBar2 = new TestInheritanceMemberItem( - lineNumber: 12, - memberName: "Class Bar2", - targets: ImmutableArray.Create( - new TargetInfo( - targetSymbolDisplayName: "Class Bar1", - locationTag: "target1", - relationship: InheritanceRelationship.Implementing), - new TargetInfo( - targetSymbolDisplayName: "Interface IBar", - locationTag: "target4", - relationship: InheritanceRelationship.Implementing))); - - var itemForFooInBar2 = new TestInheritanceMemberItem( - lineNumber: 14, - memberName: "Overrides Sub Bar2.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Sub IBar.Foo()", - locationTag: "target6", - relationship: InheritanceRelationship.Implementing), - new TargetInfo( - targetSymbolDisplayName: "Overridable Sub Bar1.Foo()", - locationTag: "target2", - relationship: InheritanceRelationship.Overriding))); - - return VerifyInSingleDocumentAsync( - testDuplicate ? markup2 : markup1, - LanguageNames.VisualBasic, - itemForIBar, - itemForFooInIBar, - itemForBar1, - itemForFooInBar1, - itemForBar2, - itemForFooInBar2); - } - - [Fact] - public Task TestVisualBasicFindGenericsBaseType() - { - var markup = @" -Public Interface {|target5:IBar|}(Of T) - Sub {|target6:Foo|}() -End Interface - -Public Class {|target1:Bar|} - Implements IBar(Of Integer) - Implements IBar(Of String) - - Public Sub {|target3:Foo|}() Implements IBar(Of Integer).Foo - Throw New NotImplementedException() - End Sub - - Private Sub {|target4:IBar_Foo|}() Implements IBar(Of String).Foo - Throw New NotImplementedException() - End Sub -End Class"; - - var itemForIBar = new TestInheritanceMemberItem( - lineNumber: 2, - memberName: "Interface IBar(Of T)", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Class Bar", - locationTag: "target1", - relationship: InheritanceRelationship.Implemented))); - - var itemForFooInIBar = new TestInheritanceMemberItem( - lineNumber: 3, - memberName: "Sub IBar(Of T).Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Sub Bar.Foo()", - locationTag: "target3", - relationship: InheritanceRelationship.Implemented), - new TargetInfo( - targetSymbolDisplayName: "Sub Bar.IBar_Foo()", - locationTag: "target4", - relationship: InheritanceRelationship.Implemented))); - - var itemForBar = new TestInheritanceMemberItem( - lineNumber: 6, - memberName: "Class Bar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Interface IBar(Of T)", - locationTag: "target5", - relationship: InheritanceRelationship.Implementing))); - - var itemForFooInBar = new TestInheritanceMemberItem( - lineNumber: 10, - memberName: "Sub Bar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Sub IBar(Of T).Foo()", - locationTag: "target6", - relationship: InheritanceRelationship.Implementing))); - - var itemForIBar_FooInBar = new TestInheritanceMemberItem( - lineNumber: 14, - memberName: "Sub Bar.IBar_Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Sub IBar(Of T).Foo()", - locationTag: "target6", - relationship: InheritanceRelationship.Implementing))); - - return VerifyInSingleDocumentAsync( - markup, - LanguageNames.VisualBasic, - itemForIBar, - itemForFooInIBar, - itemForBar, - itemForFooInBar, - itemForIBar_FooInBar); - } - - #endregion - - [Fact] - public Task TestCSharpProjectReferencingVisualBasicProject() - { - var markup1 = @" -using MyNamespace; -namespace BarNs -{ - public class {|target2:Bar|} : IBar - { - public void {|target4:Foo|}() { } - } -}"; - - var markup2 = @" -Namespace MyNamespace - Public Interface {|target1:IBar|} - Sub {|target3:Foo|}() - End Interface -End Namespace"; - - var itemForBar = new TestInheritanceMemberItem( - lineNumber: 5, - memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Interface IBar", - locationTag: "target1", - relationship: InheritanceRelationship.Implementing))); - - var itemForFooInMarkup1 = new TestInheritanceMemberItem( - lineNumber: 7, - memberName: "void Bar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Sub IBar.Foo()", - locationTag: "target3", - relationship: InheritanceRelationship.Implementing))); - - var itemForIBar = new TestInheritanceMemberItem( - lineNumber: 3, - memberName: "Interface IBar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "class Bar", - locationTag: "target2", - relationship: InheritanceRelationship.Implemented))); - - var itemForFooInMarkup2 = new TestInheritanceMemberItem( - lineNumber: 4, - memberName: "Sub IBar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "void Bar.Foo()", - locationTag: "target4", - relationship: InheritanceRelationship.Implemented))); - - return VerifyInDifferentProjectsAsync( - (markup1, LanguageNames.CSharp), - (markup2, LanguageNames.VisualBasic), - new[] { itemForBar, itemForFooInMarkup1 }, - new[] { itemForIBar, itemForFooInMarkup2 }); - } - - [Fact] - public Task TestVisualBasicProjectReferencingCSharpProject() - { - var markup1 = @" -Imports BarNs -Namespace MyNamespace - Public Class {|target2:Bar44|} - Implements IBar - - Public Sub {|target4:Foo|}() Implements IBar.Foo - End Sub - End Class -End Namespace"; - - var markup2 = @" -namespace BarNs -{ - public interface {|target1:IBar|} - { - void {|target3:Foo|}(); - } -}"; - - var itemForBar44 = new TestInheritanceMemberItem( - lineNumber: 4, - memberName: "Class Bar44", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "interface IBar", - locationTag: "target1", - relationship: InheritanceRelationship.Implementing))); - - var itemForFooInMarkup1 = new TestInheritanceMemberItem( - lineNumber: 7, - memberName: "Sub Bar44.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "void IBar.Foo()", - locationTag: "target3", - relationship: InheritanceRelationship.Implementing))); - - var itemForIBar = new TestInheritanceMemberItem( - lineNumber: 4, - memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Class Bar44", - locationTag: "target2", - relationship: InheritanceRelationship.Implemented))); - - var itemForFooInMarkup2 = new TestInheritanceMemberItem( - lineNumber: 6, - memberName: "void IBar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Sub Bar44.Foo()", - locationTag: "target4", - relationship: InheritanceRelationship.Implemented))); - - return VerifyInDifferentProjectsAsync( - (markup1, LanguageNames.VisualBasic), - (markup2, LanguageNames.CSharp), - new[] { itemForBar44, itemForFooInMarkup1 }, - new[] { itemForIBar, itemForFooInMarkup2 }); - } - } -} +//using System.Collections.Immutable; +//using System.Linq; +//using System.Security; +//using System.Threading; +//using System.Threading.Tasks; +//using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +//using Microsoft.CodeAnalysis.InheritanceMargin; +//using Microsoft.CodeAnalysis.PooledObjects; +//using Microsoft.CodeAnalysis.Shared.Extensions; +//using Microsoft.CodeAnalysis.Test.Utilities; +//using Roslyn.Utilities; +//using Xunit; + +//namespace Microsoft.CodeAnalysis.Editor.UnitTests.InheritanceMargin +//{ +// [Trait(Traits.Feature, Traits.Features.InheritanceMargin)] +// [UseExportProvider] +// public class InheritanceMarginTests +// { +// private const string SearchAreaTag = "SeachTag"; + +// #region Helpers + +// private static Task VerifyNoItemForDocumentAsync(string markup, string languageName) +// => VerifyInSingleDocumentAsync(markup, languageName); + +// private static Task VerifyInSingleDocumentAsync( +// string markup, +// string languageName, +// params TestInheritanceMemberItem[] memberItems) +// { +// var workspaceFile = $@" +// +// +// +// {markup} +// +// +//"; + +// var cancellationToken = CancellationToken.None; + +// using var testWorkspace = TestWorkspace.Create( +// workspaceFile, +// composition: EditorTestCompositions.EditorFeatures); + +// var testHostDocument = testWorkspace.Documents[0]; +// return VerifyTestMemberInDocumentAsync(testWorkspace, testHostDocument, memberItems, cancellationToken); +// } + +// private static async Task VerifyTestMemberInDocumentAsync( +// TestWorkspace testWorkspace, +// TestHostDocument testHostDocument, +// TestInheritanceMemberItem[] memberItems, +// CancellationToken cancellationToken) +// { +// var document = testWorkspace.CurrentSolution.GetRequiredDocument(testHostDocument.Id); +// var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); +// var searchingSpan = root.Span; +// // Look for the search span, if not found, then pass the whole document span to the service. +// if (testHostDocument.AnnotatedSpans.TryGetValue(SearchAreaTag, out var spans) && spans.IsSingle()) +// { +// searchingSpan = spans[0]; +// } + +// var service = document.GetRequiredLanguageService(); +// var actualItems = await service.GetInheritanceMemberItemsAsync( +// document, +// searchingSpan, +// cancellationToken).ConfigureAwait(false); + +// var sortedActualItems = actualItems.OrderBy(item => item.LineNumber).ToImmutableArray(); +// var sortedExpectedItems = memberItems.OrderBy(item => item.LineNumber).ToImmutableArray(); +// Assert.Equal(sortedExpectedItems.Length, sortedActualItems.Length); + +// for (var i = 0; i < sortedActualItems.Length; i++) +// { +// VerifyInheritanceMember(testWorkspace, sortedExpectedItems[i], sortedActualItems[i]); +// } +// } + +// private static void VerifyInheritanceMember(TestWorkspace testWorkspace, TestInheritanceMemberItem expectedItem, InheritanceMarginItem actualItem) +// { +// Assert.Equal(expectedItem.LineNumber, actualItem.LineNumber); +// Assert.Equal(expectedItem.MemberName, actualItem.DisplayTexts.JoinText()); +// Assert.Equal(expectedItem.Targets.Length, actualItem.TargetItems.Length); +// var expectedTargets = expectedItem.Targets +// .Select(info => TestInheritanceTargetItem.Create(info, testWorkspace)) +// .OrderBy(target => target.TargetSymbolName) +// .ToImmutableArray(); +// var sortedActualTargets = actualItem.TargetItems.OrderBy(target => target.DefinitionItem.DisplayParts.JoinText()) +// .ToImmutableArray(); +// for (var i = 0; i < expectedTargets.Length; i++) +// { +// VerifyInheritanceTarget(expectedTargets[i], sortedActualTargets[i]); +// } +// } + +// private static void VerifyInheritanceTarget(TestInheritanceTargetItem expectedTarget, InheritanceTargetItem actualTarget) +// { +// Assert.Equal(expectedTarget.TargetSymbolName, actualTarget.DefinitionItem.DisplayParts.JoinText()); +// Assert.Equal(expectedTarget.RelationshipToMember, actualTarget.RelationToMember); + +// if (expectedTarget.IsInMetadata) +// { +// Assert.True(actualTarget.DefinitionItem.Properties.ContainsKey("MetadataSymbolKey")); +// Assert.True(actualTarget.DefinitionItem.SourceSpans.IsEmpty); +// } +// else +// { +// var actualDocumentSpans = actualTarget.DefinitionItem.SourceSpans.OrderBy(documentSpan => documentSpan.SourceSpan.Start).ToImmutableArray(); +// var expectedDocumentSpans = expectedTarget.DocumentSpans.OrderBy(documentSpan => documentSpan.SourceSpan.Start).ToImmutableArray(); +// Assert.Equal(expectedDocumentSpans.Length, actualDocumentSpans.Length); +// for (var i = 0; i < actualDocumentSpans.Length; i++) +// { +// Assert.Equal(expectedDocumentSpans[i].SourceSpan, actualDocumentSpans[i].SourceSpan); +// Assert.Equal(expectedDocumentSpans[i].Document.FilePath, actualDocumentSpans[i].Document.FilePath); +// } +// } +// } + +// /// +// /// Project of markup1 is referencing project of markup2 +// /// +// private static async Task VerifyInDifferentProjectsAsync( +// (string markupInProject1, string languageName) markup1, +// (string markupInProject2, string languageName) markup2, +// TestInheritanceMemberItem[] memberItemsInMarkup1, +// TestInheritanceMemberItem[] memberItemsInMarkup2) +// { +// var workspaceFile = +// $@" +// +// +// Assembly2 +// +// {markup1.markupInProject1} +// +// +// +// +// {markup2.markupInProject2} +// +// +//"; + +// var cancellationToken = CancellationToken.None; +// using var testWorkspace = TestWorkspace.Create( +// workspaceFile, +// composition: EditorTestCompositions.EditorFeatures); + +// var testHostDocument1 = testWorkspace.Documents.Single(doc => doc.Project.AssemblyName.Equals("Assembly1")); +// var testHostDocument2 = testWorkspace.Documents.Single(doc => doc.Project.AssemblyName.Equals("Assembly2")); +// await VerifyTestMemberInDocumentAsync(testWorkspace, testHostDocument1, memberItemsInMarkup1, cancellationToken).ConfigureAwait(false); +// await VerifyTestMemberInDocumentAsync(testWorkspace, testHostDocument2, memberItemsInMarkup2, cancellationToken).ConfigureAwait(false); +// } + +// private class TestInheritanceMemberItem +// { +// public readonly int LineNumber; +// public readonly string MemberName; +// public readonly ImmutableArray Targets; + +// public TestInheritanceMemberItem( +// int lineNumber, +// string memberName, +// ImmutableArray targets) +// { +// LineNumber = lineNumber; +// MemberName = memberName; +// Targets = targets; +// } +// } + +// private class TargetInfo +// { +// public readonly string TargetSymbolDisplayName; +// public readonly string? LocationTag; +// public readonly InheritanceRelationship Relationship; +// public readonly bool InMetadata; + +// public TargetInfo( +// string targetSymbolDisplayName, +// string locationTag, +// InheritanceRelationship relationship) +// { +// TargetSymbolDisplayName = targetSymbolDisplayName; +// LocationTag = locationTag; +// Relationship = relationship; +// InMetadata = false; +// } + +// public TargetInfo( +// string targetSymbolDisplayName, +// InheritanceRelationship relationship, +// bool inMetadata) +// { +// TargetSymbolDisplayName = targetSymbolDisplayName; +// Relationship = relationship; +// InMetadata = inMetadata; +// LocationTag = null; +// } +// } + +// private class TestInheritanceTargetItem +// { +// public readonly string TargetSymbolName; +// public readonly InheritanceRelationship RelationshipToMember; +// public readonly ImmutableArray DocumentSpans; +// public readonly bool IsInMetadata; + +// public TestInheritanceTargetItem( +// string targetSymbolName, +// InheritanceRelationship relationshipToMember, +// ImmutableArray documentSpans, +// bool isInMetadata) +// { +// TargetSymbolName = targetSymbolName; +// RelationshipToMember = relationshipToMember; +// DocumentSpans = documentSpans; +// IsInMetadata = isInMetadata; +// } + +// public static TestInheritanceTargetItem Create( +// TargetInfo targetInfo, +// TestWorkspace testWorkspace) +// { +// if (targetInfo.InMetadata) +// { +// return new TestInheritanceTargetItem( +// targetInfo.TargetSymbolDisplayName, +// targetInfo.Relationship, +// ImmutableArray.Empty, +// isInMetadata: true); +// } +// else +// { +// using var _ = ArrayBuilder.GetInstance(out var builder); +// // If the target is not in metadata, there must be a location tag to give the span! +// Assert.True(targetInfo.LocationTag != null); +// foreach (var testHostDocument in testWorkspace.Documents) +// { +// if (targetInfo.LocationTag != null) +// { +// var annotatedSpans = testHostDocument.AnnotatedSpans; +// if (annotatedSpans.TryGetValue(targetInfo.LocationTag, out var spans)) +// { +// var document = testWorkspace.CurrentSolution.GetRequiredDocument(testHostDocument.Id); +// builder.AddRange(spans.Select(span => new DocumentSpan(document, span))); +// } +// } +// } + +// return new TestInheritanceTargetItem( +// targetInfo.TargetSymbolDisplayName, +// targetInfo.Relationship, +// builder.ToImmutable(), +// isInMetadata: false); +// } +// } +// } + +// #endregion + +// #region TestsForCSharp + +// [Fact] +// public Task TestCSharpClassWithErrorBaseType() +// { +// var markup = @" +//public class Bar : SomethingUnknown +//{ +//}"; +// return VerifyNoItemForDocumentAsync(markup, LanguageNames.CSharp); +// } + +// [Fact] +// public Task TestCSharpReferencingMetadata() +// { +// var markup = @" +//using System.Collections; +//public class Bar : IEnumerable +//{ +// public IEnumerator GetEnumerator () { return null }; +//}"; +// var itemForBar = new TestInheritanceMemberItem( +// lineNumber: 3, +// memberName: "class Bar", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "interface IEnumerable", +// relationship: InheritanceRelationship.Implementing, +// inMetadata: true))); + +// var itemForGetEnumerator = new TestInheritanceMemberItem( +// lineNumber: 5, +// memberName: "IEnumerator Bar.GetEnumerator()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "IEnumerator IEnumerable.GetEnumerator()", +// relationship: InheritanceRelationship.Implementing, +// inMetadata: true))); + +// return VerifyInSingleDocumentAsync(markup, LanguageNames.CSharp, itemForBar, itemForGetEnumerator); +// } + +// [Fact] +// public Task TestCSharpClassImplementingInterface() +// { +// var markup = @" +//interface {|target1:IBar|} { } +//public class {|target2:Bar|} : IBar +//{ +//} +// "; + +// var itemOnLine2 = new TestInheritanceMemberItem( +// lineNumber: 2, +// memberName: "interface IBar", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "class Bar", +// locationTag: "target2", +// relationship: InheritanceRelationship.Implemented))); + +// var itemOnLine3 = new TestInheritanceMemberItem( +// lineNumber: 3, +// memberName: "class Bar", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "interface IBar", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implementing))); + +// return VerifyInSingleDocumentAsync( +// markup, +// LanguageNames.CSharp, +// itemOnLine2, +// itemOnLine3); +// } + +// [Fact] +// public Task TestCSharpInterfaceImplementingInterface() +// { +// var markup = @" +//interface {|target1:IBar|} { } +//interface {|target2:IBar2|} : IBar { } +// "; + +// var itemOnLine2 = new TestInheritanceMemberItem( +// lineNumber: 2, +// memberName: "interface IBar", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "interface IBar2", +// locationTag: "target2", +// relationship: InheritanceRelationship.Implemented)) +// ); +// var itemOnLine3 = new TestInheritanceMemberItem( +// lineNumber: 3, +// memberName: "interface IBar2", +// targets: ImmutableArray.Empty +// .Add(new TargetInfo( +// targetSymbolDisplayName: "interface IBar", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implementing)) +// ); + +// return VerifyInSingleDocumentAsync( +// markup, +// LanguageNames.CSharp, +// itemOnLine2, +// itemOnLine3); +// } + +// [Fact] +// public Task TestCSharpClassInheritsClass() +// { +// var markup = @" +//class {|target2:A|} { } +//class {|target1:B|} : A { } +// "; + +// var itemOnLine2 = new TestInheritanceMemberItem( +// lineNumber: 2, +// memberName: "class A", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "class B", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implemented)) +// ); +// var itemOnLine3 = new TestInheritanceMemberItem( +// lineNumber: 3, +// memberName: "class B", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "class A", +// locationTag: "target2", +// relationship: InheritanceRelationship.Implementing)) +// ); + +// return VerifyInSingleDocumentAsync( +// markup, +// LanguageNames.CSharp, +// itemOnLine2, +// itemOnLine3); +// } + +// [Theory] +// [InlineData("class")] +// [InlineData("struct")] +// [InlineData("enum")] +// [InlineData("interface")] +// public Task TestCSharpTypeWithoutBaseType(string typeName) +// { +// var markup = $@" +//public {typeName} Bar +//{{ +//}}"; +// return VerifyNoItemForDocumentAsync(markup, LanguageNames.CSharp); +// } + +// [Theory] +// [InlineData("public Bar() { }")] +// [InlineData("public static void Bar3() { }")] +// [InlineData("public static void ~Bar() { }")] +// [InlineData("public static Bar operator +(Bar a, Bar b) => new Bar();")] +// public Task TestCSharpSpecialMember(string memberDeclaration) +// { +// var markup = $@" +//public abstract class {{|target1:Bar1|}} +//{{}} +//public class Bar : Bar1 +//{{ +// {{|{SearchAreaTag}:{memberDeclaration}|}} +//}}"; +// return VerifyInSingleDocumentAsync( +// markup, +// LanguageNames.CSharp, +// new TestInheritanceMemberItem( +// lineNumber: 4, +// memberName: "class Bar", +// targets: ImmutableArray.Create( +// new TargetInfo( +// targetSymbolDisplayName: "class Bar1", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implementing)))); +// } + +// [Fact] +// public Task TestCSharpMetadataInterface() +// { +// var markup = @" +//using System.Collections; +//public class Bar : IEnumerable +//{ +//}"; +// return VerifyInSingleDocumentAsync( +// markup, +// LanguageNames.CSharp, +// new TestInheritanceMemberItem( +// lineNumber: 3, +// memberName: "class Bar", +// targets: ImmutableArray.Create( +// new TargetInfo( +// targetSymbolDisplayName: "interface IEnumerable", +// relationship: InheritanceRelationship.Implementing, +// inMetadata: true)))); +// } + +// [Fact] +// public Task TestCSharpEventDeclaration() +// { +// var markup = @" +//using System; +//interface {|target2:IBar|} +//{ +// event EventHandler {|target4:e|}; +//} +//public class {|target1:Bar|} : IBar +//{ +// public event EventHandler {|target3:e|} +// { +// add {} remove {} +// } +//}"; +// var itemForIBar = new TestInheritanceMemberItem( +// lineNumber: 3, +// memberName: "interface IBar", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "class Bar", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForBar = new TestInheritanceMemberItem( +// lineNumber: 7, +// memberName: "class Bar", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "interface IBar", +// locationTag: "target2", +// relationship: InheritanceRelationship.Implementing))); + +// var itemForEventInInterface = new TestInheritanceMemberItem( +// lineNumber: 5, +// memberName: "event EventHandler IBar.e", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "event EventHandler Bar.e", +// locationTag: "target3", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForEventInClass = new TestInheritanceMemberItem( +// lineNumber: 9, +// memberName: "event EventHandler Bar.e", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "event EventHandler IBar.e", +// locationTag: "target4", +// relationship: InheritanceRelationship.Implementing))); + +// return VerifyInSingleDocumentAsync( +// markup, +// LanguageNames.CSharp, +// itemForIBar, +// itemForBar, +// itemForEventInInterface, +// itemForEventInClass); +// } + +// [Fact] +// public Task TestCSharpEventFieldDeclarations() +// { +// var markup = @"using System; +//interface {|target2:IBar|} +//{ +// event EventHandler {|target5:e1|}, {|target6:e2|}; +//} +//public class {|target1:Bar|} : IBar +//{ +// public event EventHandler {|target3:e1|}, {|target4:e2|}; +//}"; +// var itemForIBar = new TestInheritanceMemberItem( +// lineNumber: 2, +// memberName: "interface IBar", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "class Bar", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForBar = new TestInheritanceMemberItem( +// lineNumber: 6, +// memberName: "class Bar", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "interface IBar", +// locationTag: "target2", +// relationship: InheritanceRelationship.Implementing))); + +// var itemForE1InInterface = new TestInheritanceMemberItem( +// lineNumber: 4, +// memberName: "event EventHandler IBar.e1", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "event EventHandler Bar.e1", +// locationTag: "target3", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForE2InInterface = new TestInheritanceMemberItem( +// lineNumber: 4, +// memberName: "event EventHandler IBar.e2", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "event EventHandler Bar.e2", +// locationTag: "target4", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForE1InClass = new TestInheritanceMemberItem( +// lineNumber: 8, +// memberName: "event EventHandler Bar.e1", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "event EventHandler IBar.e1", +// locationTag: "target5", +// relationship: InheritanceRelationship.Implementing))); + +// var itemForE2InClass = new TestInheritanceMemberItem( +// lineNumber: 8, +// memberName: "event EventHandler Bar.e2", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "event EventHandler IBar.e2", +// locationTag: "target6", +// relationship: InheritanceRelationship.Implementing))); + +// return VerifyInSingleDocumentAsync( +// markup, +// LanguageNames.CSharp, +// itemForIBar, +// itemForBar, +// itemForE1InInterface, +// itemForE2InInterface, +// itemForE1InClass, +// itemForE2InClass); +// } + +// [Fact] +// public Task TestCSharpInterfaceMembers() +// { +// var markup = @"using System; +//interface {|target1:IBar|} +//{ +// void {|target4:Foo|}(); +// int {|target6:Poo|} { get; set; } +// event EventHandler {|target8:Eoo|}; +// int {|target9:this|}[int i] { get; set; } +//} +//public class {|target2:Bar|} : IBar +//{ +// public void {|target3:Foo|}() { } +// public int {|target5:Poo|} { get; set; } +// public event EventHandler {|target7:Eoo|}; +// public int {|target10:this|}[int i] { get => 1; set { } } +//}"; +// var itemForEooInClass = new TestInheritanceMemberItem( +// lineNumber: 13, +// memberName: "event EventHandler Bar.Eoo", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "event EventHandler IBar.Eoo", +// locationTag: "target8", +// relationship: InheritanceRelationship.Implementing)) +// ); + +// var itemForEooInInterface = new TestInheritanceMemberItem( +// lineNumber: 6, +// memberName: "event EventHandler IBar.Eoo", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "event EventHandler Bar.Eoo", +// locationTag: "target7", +// relationship: InheritanceRelationship.Implemented)) +// ); + +// var itemForPooInInterface = new TestInheritanceMemberItem( +// lineNumber: 5, +// memberName: "int IBar.Poo { get; set; }", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "int Bar.Poo { get; set; }", +// locationTag: "target5", +// relationship: InheritanceRelationship.Implemented)) +// ); + +// var itemForPooInClass = new TestInheritanceMemberItem( +// lineNumber: 12, +// memberName: "int Bar.Poo { get; set; }", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "int IBar.Poo { get; set; }", +// locationTag: "target6", +// relationship: InheritanceRelationship.Implementing)) +// ); + +// var itemForFooInInterface = new TestInheritanceMemberItem( +// lineNumber: 4, +// memberName: "void IBar.Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "void Bar.Foo()", +// locationTag: "target3", +// relationship: InheritanceRelationship.Implemented)) +// ); + +// var itemForFooInClass = new TestInheritanceMemberItem( +// lineNumber: 11, +// memberName: "void Bar.Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "void IBar.Foo()", +// locationTag: "target4", +// relationship: InheritanceRelationship.Implementing)) +// ); + +// var itemForIBar = new TestInheritanceMemberItem( +// lineNumber: 2, +// memberName: "interface IBar", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "class Bar", +// locationTag: "target2", +// relationship: InheritanceRelationship.Implemented)) +// ); + +// var itemForBar = new TestInheritanceMemberItem( +// lineNumber: 9, +// memberName: "class Bar", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "interface IBar", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implementing)) +// ); + +// var itemForIndexerInClass = new TestInheritanceMemberItem( +// lineNumber: 14, +// memberName: "int Bar.this[int] { get; set; }", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "int IBar.this[int] { get; set; }", +// locationTag: "target9", +// relationship: InheritanceRelationship.Implementing)) +// ); + +// var itemForIndexerInInterface = new TestInheritanceMemberItem( +// lineNumber: 7, +// memberName: "int IBar.this[int] { get; set; }", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "int Bar.this[int] { get; set; }", +// locationTag: "target10", +// relationship: InheritanceRelationship.Implemented)) +// ); + +// return VerifyInSingleDocumentAsync( +// markup, +// LanguageNames.CSharp, +// itemForEooInClass, +// itemForEooInInterface, +// itemForPooInInterface, +// itemForPooInClass, +// itemForFooInInterface, +// itemForFooInClass, +// itemForIBar, +// itemForBar, +// itemForIndexerInInterface, +// itemForIndexerInClass); +// } + +// [Theory] +// [InlineData("abstract")] +// [InlineData("virtual")] +// public Task TestCSharpAbstractClassMembers(string modifier) +// { +// var markup = $@"using System; +//public abstract class {{|target2:Bar|}} +//{{ +// public {modifier} void {{|target4:Foo|}}(); +// public {modifier} int {{|target6:Poo|}} {{ get; set; }} +// public {modifier} event EventHandler {{|target8:Eoo|}}; +//}} +//public class {{|target1:Bar2|}} : Bar +//{{ +// public override void {{|target3:Foo|}}() {{ }} +// public override int {{|target5:Poo|}} {{ get; set; }} +// public override event EventHandler {{|target7:Eoo|}}; +//}} +// "; + +// var itemForEooInClass = new TestInheritanceMemberItem( +// lineNumber: 12, +// memberName: "override event EventHandler Bar2.Eoo", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: $"{modifier} event EventHandler Bar.Eoo", +// locationTag: "target8", +// relationship: InheritanceRelationship.Overriding))); + +// var itemForEooInAbstractClass = new TestInheritanceMemberItem( +// lineNumber: 6, +// memberName: $"{modifier} event EventHandler Bar.Eoo", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "override event EventHandler Bar2.Eoo", +// locationTag: "target7", +// relationship: InheritanceRelationship.Overridden))); + +// var itemForPooInClass = new TestInheritanceMemberItem( +// lineNumber: 11, +// memberName: "override int Bar2.Poo { get; set; }", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: $"{modifier} int Bar.Poo {{ get; set; }}", +// locationTag: "target6", +// relationship: InheritanceRelationship.Overriding))); + +// var itemForPooInAbstractClass = new TestInheritanceMemberItem( +// lineNumber: 5, +// memberName: $"{modifier} int Bar.Poo {{ get; set; }}", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "override int Bar2.Poo { get; set; }", +// locationTag: "target5", +// relationship: InheritanceRelationship.Overridden))); + +// var itemForFooInAbstractClass = new TestInheritanceMemberItem( +// lineNumber: 4, +// memberName: $"{modifier} void Bar.Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "override void Bar2.Foo()", +// locationTag: "target3", +// relationship: InheritanceRelationship.Overridden))); + +// var itemForFooInClass = new TestInheritanceMemberItem( +// lineNumber: 10, +// memberName: "override void Bar2.Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: $"{modifier} void Bar.Foo()", +// locationTag: "target4", +// relationship: InheritanceRelationship.Overriding))); + +// var itemForBar = new TestInheritanceMemberItem( +// lineNumber: 2, +// memberName: "class Bar", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "class Bar2", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForBar2 = new TestInheritanceMemberItem( +// lineNumber: 8, +// memberName: "class Bar2", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "class Bar", +// locationTag: "target2", +// relationship: InheritanceRelationship.Implementing))); + +// return VerifyInSingleDocumentAsync( +// markup, +// LanguageNames.CSharp, +// itemForBar, +// itemForBar2, +// itemForFooInAbstractClass, +// itemForFooInClass, +// itemForPooInClass, +// itemForPooInAbstractClass, +// itemForEooInClass, +// itemForEooInAbstractClass); +// } + +// [Theory] +// [CombinatorialData] +// public Task TestCSharpOverrideMemberCanFindImplementingInterface(bool testDuplicate) +// { +// var markup1 = @"using System; +//public interface {|target4:IBar|} +//{ +// void {|target6:Foo|}(); +//} +//public class {|target1:Bar1|} : IBar +//{ +// public virtual void {|target2:Foo|}() { } +//} +//public class {|target5:Bar2|} : Bar1 +//{ +// public override void {|target3:Foo|}() { } +//}"; + +// var markup2 = @"using System; +//public interface {|target4:IBar|} +//{ +// void {|target6:Foo|}(); +//} +//public class {|target1:Bar1|} : IBar +//{ +// public virtual void {|target2:Foo|}() { } +//} +//public class {|target5:Bar2|} : Bar1, IBar +//{ +// public override void {|target3:Foo|}() { } +//}"; + +// var itemForIBar = new TestInheritanceMemberItem( +// lineNumber: 2, +// memberName: "interface IBar", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "class Bar1", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implemented), +// new TargetInfo( +// targetSymbolDisplayName: "class Bar2", +// locationTag: "target5", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForFooInIBar = new TestInheritanceMemberItem( +// lineNumber: 4, +// memberName: "void IBar.Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "virtual void Bar1.Foo()", +// locationTag: "target2", +// relationship: InheritanceRelationship.Implemented), +// new TargetInfo( +// targetSymbolDisplayName: "override void Bar2.Foo()", +// locationTag: "target3", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForBar1 = new TestInheritanceMemberItem( +// lineNumber: 6, +// memberName: "class Bar1", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "interface IBar", +// locationTag: "target4", +// relationship: InheritanceRelationship.Implementing), +// new TargetInfo( +// targetSymbolDisplayName: "class Bar2", +// locationTag: "target5", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForFooInBar1 = new TestInheritanceMemberItem( +// lineNumber: 8, +// memberName: "virtual void Bar1.Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "void IBar.Foo()", +// locationTag: "target6", +// relationship: InheritanceRelationship.Implementing), +// new TargetInfo( +// targetSymbolDisplayName: "override void Bar2.Foo()", +// locationTag: "target3", +// relationship: InheritanceRelationship.Overridden))); + +// var itemForBar2 = new TestInheritanceMemberItem( +// lineNumber: 10, +// memberName: "class Bar2", +// targets: ImmutableArray.Create( +// new TargetInfo( +// targetSymbolDisplayName: "class Bar1", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implementing), +// new TargetInfo( +// targetSymbolDisplayName: "interface IBar", +// locationTag: "target4", +// relationship: InheritanceRelationship.Implementing))); + +// var itemForFooInBar2 = new TestInheritanceMemberItem( +// lineNumber: 12, +// memberName: "override void Bar2.Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "void IBar.Foo()", +// locationTag: "target6", +// relationship: InheritanceRelationship.Implementing), +// new TargetInfo( +// targetSymbolDisplayName: "virtual void Bar1.Foo()", +// locationTag: "target2", +// relationship: InheritanceRelationship.Overriding))); + +// return VerifyInSingleDocumentAsync( +// testDuplicate ? markup2 : markup1, +// LanguageNames.CSharp, +// itemForIBar, +// itemForFooInIBar, +// itemForBar1, +// itemForFooInBar1, +// itemForBar2, +// itemForFooInBar2); +// } + +// [Fact] +// public Task TestCSharpFindGenericsBaseType() +// { +// var lessThanToken = SecurityElement.Escape("<"); +// var greaterThanToken = SecurityElement.Escape(">"); +// var markup = $@" +//public interface {{|target2:IBar|}}{lessThanToken}T{greaterThanToken} +//{{ +// void {{|target4:Foo|}}(); +//}} + +//public class {{|target1:Bar2|}} : IBar{lessThanToken}int{greaterThanToken}, IBar{lessThanToken}string{greaterThanToken} +//{{ +// public void {{|target3:Foo|}}(); +//}}"; + +// var itemForIBar = new TestInheritanceMemberItem( +// lineNumber: 2, +// memberName: "interface IBar", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "class Bar2", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForFooInIBar = new TestInheritanceMemberItem( +// lineNumber: 4, +// memberName: "void IBar.Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "void Bar2.Foo()", +// locationTag: "target3", +// relationship: InheritanceRelationship.Implemented))); + +// // Only have one IBar item +// var itemForBar2 = new TestInheritanceMemberItem( +// lineNumber: 7, +// memberName: "class Bar2", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "interface IBar", +// locationTag: "target2", +// relationship: InheritanceRelationship.Implementing))); + +// // Only have one IBar.Foo item +// var itemForFooInBar2 = new TestInheritanceMemberItem( +// lineNumber: 9, +// memberName: "void Bar2.Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "void IBar.Foo()", +// locationTag: "target4", +// relationship: InheritanceRelationship.Implementing))); + +// return VerifyInSingleDocumentAsync( +// markup, +// LanguageNames.CSharp, +// itemForIBar, +// itemForFooInIBar, +// itemForBar2, +// itemForFooInBar2); +// } + +// #endregion + +// #region TestsForVisualBasic + +// [Fact] +// public Task TestVisualBasicWithErrorBaseType() +// { +// var markup = @" +//Namespace MyNamespace +// Public Class Bar +// Implements SomethingNotExist +// End Class +//End Namespace"; + +// return VerifyNoItemForDocumentAsync(markup, LanguageNames.VisualBasic); +// } + +// [Fact] +// public Task TestVisualBasicReferencingMetadata() +// { +// var markup = @" +//Namespace MyNamespace +// Public Class Bar +// Implements System.Collections.IEnumerable +// Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator +// Throw New NotImplementedException() +// End Function +// End Class +//End Namespace"; +// var itemForBar = new TestInheritanceMemberItem( +// lineNumber: 3, +// memberName: "Class Bar", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Interface IEnumerable", +// relationship: InheritanceRelationship.Implementing, +// inMetadata: true))); + +// var itemForGetEnumerator = new TestInheritanceMemberItem( +// lineNumber: 5, +// memberName: "Function Bar.GetEnumerator() As IEnumerator", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Function IEnumerable.GetEnumerator() As IEnumerator", +// relationship: InheritanceRelationship.Implementing, +// inMetadata: true))); + +// return VerifyInSingleDocumentAsync(markup, LanguageNames.VisualBasic, itemForBar, itemForGetEnumerator); +// } + +// [Fact] +// public Task TestVisualBasicClassImplementingInterface() +// { +// var markup = @" +//Interface {|target2:IBar|} +//End Interface +//Class {|target1:Bar|} +// Implements IBar +//End Class"; +// var itemForIBar = new TestInheritanceMemberItem( +// lineNumber: 2, +// memberName: "Interface IBar", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Class Bar", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForBar = new TestInheritanceMemberItem( +// lineNumber: 4, +// memberName: "Class Bar", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Interface IBar", +// locationTag: "target2", +// relationship: InheritanceRelationship.Implementing))); + +// return VerifyInSingleDocumentAsync( +// markup, +// LanguageNames.VisualBasic, +// itemForIBar, +// itemForBar); +// } + +// [Fact] +// public Task TestVisualBasicInterfaceImplementingInterface() +// { +// var markup = @" +//Interface {|target2:IBar2|} +//End Interface +//Interface {|target1:IBar|} +// Inherits IBar2 +//End Interface"; + +// var itemForIBar2 = new TestInheritanceMemberItem( +// lineNumber: 2, +// memberName: "Interface IBar2", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Interface IBar", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForIBar = new TestInheritanceMemberItem( +// lineNumber: 4, +// memberName: "Interface IBar", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Interface IBar2", +// locationTag: "target2", +// relationship: InheritanceRelationship.Implementing))); +// return VerifyInSingleDocumentAsync(markup, LanguageNames.VisualBasic, itemForIBar2, itemForIBar); +// } + +// [Fact] +// public Task TestVisualBasicClassInheritsClass() +// { +// var markup = @" +//Class {|target2:Bar2|} +//End Class +//Class {|target1:Bar|} +// Inherits Bar2 +//End Class"; + +// var itemForBar2 = new TestInheritanceMemberItem( +// lineNumber: 2, +// memberName: "Class Bar2", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Class Bar", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForBar = new TestInheritanceMemberItem( +// lineNumber: 4, +// memberName: "Class Bar", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Class Bar2", +// locationTag: "target2", +// relationship: InheritanceRelationship.Implementing))); +// return VerifyInSingleDocumentAsync(markup, LanguageNames.VisualBasic, itemForBar2, itemForBar); +// } + +// [Theory] +// [InlineData("Class")] +// [InlineData("Structure")] +// [InlineData("Enum")] +// [InlineData("Interface")] +// public Task TestVisualBasicTypeWithoutBaseType(string typeName) +// { +// var markup = $@" +//{typeName} Bar +//End {typeName}"; + +// return VerifyNoItemForDocumentAsync(markup, LanguageNames.VisualBasic); +// } + +// [Fact] +// public Task TestVisualBasicMetadataInterface() +// { +// var markup = @" +//Imports System.Collections +//Class Bar +// Implements IEnumerable +//End Class"; +// return VerifyInSingleDocumentAsync( +// markup, +// LanguageNames.VisualBasic, +// new TestInheritanceMemberItem( +// lineNumber: 3, +// memberName: "Class Bar", +// targets: ImmutableArray.Create( +// new TargetInfo( +// targetSymbolDisplayName: "Interface IEnumerable", +// relationship: InheritanceRelationship.Implementing, +// inMetadata: true)))); +// } + +// [Fact] +// public Task TestVisualBasicEventStatement() +// { +// var markup = @" +//Interface {|target2:IBar|} +// Event {|target4:e|} As EventHandler +//End Interface +//Class {|target1:Bar|} +// Implements IBar +// Public Event {|target3:e|} As EventHandler Implements IBar.e +//End Class"; + +// var itemForIBar = new TestInheritanceMemberItem( +// lineNumber: 2, +// memberName: "Interface IBar", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Class Bar", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForBar = new TestInheritanceMemberItem( +// lineNumber: 5, +// memberName: "Class Bar", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Interface IBar", +// locationTag: "target2", +// relationship: InheritanceRelationship.Implementing))); + +// var itemForEventInInterface = new TestInheritanceMemberItem( +// lineNumber: 3, +// memberName: "Event IBar.e As EventHandler", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Event Bar.e As EventHandler", +// locationTag: "target3", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForEventInClass = new TestInheritanceMemberItem( +// lineNumber: 7, +// memberName: "Event Bar.e As EventHandler", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Event IBar.e As EventHandler", +// locationTag: "target4", +// relationship: InheritanceRelationship.Implementing))); + +// return VerifyInSingleDocumentAsync( +// markup, +// LanguageNames.VisualBasic, +// itemForIBar, +// itemForBar, +// itemForEventInInterface, +// itemForEventInClass); +// } + +// [Fact] +// public Task TestVisualBasicEventBlock() +// { +// var markup = @" +//Interface {|target2:IBar|} +// Event {|target4:e|} As EventHandler +//End Interface +//Class {|target1:Bar|} +// Implements IBar +// Public Custom Event {|target3:e|} As EventHandler Implements IBar.e +// End Event +//End Class"; +// var itemForIBar = new TestInheritanceMemberItem( +// lineNumber: 2, +// memberName: "Interface IBar", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Class Bar", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForBar = new TestInheritanceMemberItem( +// lineNumber: 5, +// memberName: "Class Bar", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Interface IBar", +// locationTag: "target2", +// relationship: InheritanceRelationship.Implementing))); + +// var itemForEventInInterface = new TestInheritanceMemberItem( +// lineNumber: 3, +// memberName: "Event IBar.e As EventHandler", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Event Bar.e As EventHandler", +// locationTag: "target3", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForEventInClass = new TestInheritanceMemberItem( +// lineNumber: 7, +// memberName: "Event Bar.e As EventHandler", +// ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Event IBar.e As EventHandler", +// locationTag: "target4", +// relationship: InheritanceRelationship.Implementing))); + +// return VerifyInSingleDocumentAsync( +// markup, +// LanguageNames.VisualBasic, +// itemForIBar, +// itemForBar, +// itemForEventInInterface, +// itemForEventInClass); +// } + +// [Fact] +// public Task TestVisualBasicInterfaceMembers() +// { +// var markup = @" +//Interface {|target2:IBar|} +// Property {|target4:Poo|} As Integer +// Function {|target6:Foo|}() As Integer +//End Interface + +//Class {|target1:Bar|} +// Implements IBar +// Public Property {|target3:Poo|} As Integer Implements IBar.Poo +// Get +// Return 1 +// End Get +// Set(value As Integer) +// End Set +// End Property +// Public Function {|target5:Foo|}() As Integer Implements IBar.Foo +// Return 1 +// End Function +//End Class"; +// var itemForIBar = new TestInheritanceMemberItem( +// lineNumber: 2, +// memberName: "Interface IBar", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Class Bar", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForBar = new TestInheritanceMemberItem( +// lineNumber: 7, +// memberName: "Class Bar", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Interface IBar", +// locationTag: "target2", +// relationship: InheritanceRelationship.Implementing))); + +// var itemForPooInInterface = new TestInheritanceMemberItem( +// lineNumber: 3, +// memberName: "Property IBar.Poo As Integer", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Property Bar.Poo As Integer", +// locationTag: "target3", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForPooInClass = new TestInheritanceMemberItem( +// lineNumber: 9, +// memberName: "Property Bar.Poo As Integer", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Property IBar.Poo As Integer", +// locationTag: "target4", +// relationship: InheritanceRelationship.Implementing))); + +// var itemForFooInInterface = new TestInheritanceMemberItem( +// lineNumber: 4, +// memberName: "Function IBar.Foo() As Integer", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Function Bar.Foo() As Integer", +// locationTag: "target5", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForFooInClass = new TestInheritanceMemberItem( +// lineNumber: 16, +// memberName: "Function Bar.Foo() As Integer", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Function IBar.Foo() As Integer", +// locationTag: "target6", +// relationship: InheritanceRelationship.Implementing))); + +// return VerifyInSingleDocumentAsync( +// markup, +// LanguageNames.VisualBasic, +// itemForIBar, +// itemForBar, +// itemForPooInInterface, +// itemForPooInClass, +// itemForFooInInterface, +// itemForFooInClass); +// } + +// [Fact] +// public Task TestVisualBasicMustInheritClassMember() +// { +// var markup = @" +//MustInherit Class {|target2:Bar1|} +// Public MustOverride Sub {|target4:Foo|}() +//End Class + +//Class {|target1:Bar|} +// Inherits Bar1 +// Public Overrides Sub {|target3:Foo|}() +// End Sub +//End Class"; +// var itemForBar1 = new TestInheritanceMemberItem( +// lineNumber: 2, +// memberName: "Class Bar1", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: $"Class Bar", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForBar = new TestInheritanceMemberItem( +// lineNumber: 6, +// memberName: "Class Bar", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Class Bar1", +// locationTag: "target2", +// relationship: InheritanceRelationship.Implementing))); + +// var itemForFooInBar1 = new TestInheritanceMemberItem( +// lineNumber: 3, +// memberName: "MustOverride Sub Bar1.Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Overrides Sub Bar.Foo()", +// locationTag: "target3", +// relationship: InheritanceRelationship.Overridden))); + +// var itemForFooInBar = new TestInheritanceMemberItem( +// lineNumber: 8, +// memberName: "Overrides Sub Bar.Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "MustOverride Sub Bar1.Foo()", +// locationTag: "target4", +// relationship: InheritanceRelationship.Overriding))); + +// return VerifyInSingleDocumentAsync( +// markup, +// LanguageNames.VisualBasic, +// itemForBar1, +// itemForBar, +// itemForFooInBar1, +// itemForFooInBar); +// } + +// [Theory] +// [CombinatorialData] +// public Task TestVisualBasicOverrideMemberCanFindImplementingInterface(bool testDuplicate) +// { +// var markup1 = @" +//Interface {|target4:IBar|} +// Sub {|target6:Foo|}() +//End Interface + +//Class {|target1:Bar1|} +// Implements IBar +// Public Overridable Sub {|target2:Foo|}() Implements IBar.Foo +// End Sub +//End Class + +//Class {|target5:Bar2|} +// Inherits Bar1 +// Public Overrides Sub {|target3:Foo|}() +// End Sub +//End Class"; + +// var markup2 = @" +//Interface {|target4:IBar|} +// Sub {|target6:Foo|}() +//End Interface + +//Class {|target1:Bar1|} +// Implements IBar +// Public Overridable Sub {|target2:Foo|}() Implements IBar.Foo +// End Sub +//End Class + +//Class {|target5:Bar2|} +// Inherits Bar1 +// Public Overrides Sub {|target3:Foo|}() +// End Sub +//End Class"; +// var itemForIBar = new TestInheritanceMemberItem( +// lineNumber: 2, +// memberName: "Interface IBar", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Class Bar1", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implemented), +// new TargetInfo( +// targetSymbolDisplayName: "Class Bar2", +// locationTag: "target5", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForFooInIBar = new TestInheritanceMemberItem( +// lineNumber: 3, +// memberName: "Sub IBar.Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Overridable Sub Bar1.Foo()", +// locationTag: "target2", +// relationship: InheritanceRelationship.Implemented), +// new TargetInfo( +// targetSymbolDisplayName: "Overrides Sub Bar2.Foo()", +// locationTag: "target3", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForBar1 = new TestInheritanceMemberItem( +// lineNumber: 6, +// memberName: "Class Bar1", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Interface IBar", +// locationTag: "target4", +// relationship: InheritanceRelationship.Implementing), +// new TargetInfo( +// targetSymbolDisplayName: "Class Bar2", +// locationTag: "target5", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForFooInBar1 = new TestInheritanceMemberItem( +// lineNumber: 8, +// memberName: "Overridable Sub Bar1.Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Sub IBar.Foo()", +// locationTag: "target6", +// relationship: InheritanceRelationship.Implementing), +// new TargetInfo( +// targetSymbolDisplayName: "Overrides Sub Bar2.Foo()", +// locationTag: "target3", +// relationship: InheritanceRelationship.Overridden))); + +// var itemForBar2 = new TestInheritanceMemberItem( +// lineNumber: 12, +// memberName: "Class Bar2", +// targets: ImmutableArray.Create( +// new TargetInfo( +// targetSymbolDisplayName: "Class Bar1", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implementing), +// new TargetInfo( +// targetSymbolDisplayName: "Interface IBar", +// locationTag: "target4", +// relationship: InheritanceRelationship.Implementing))); + +// var itemForFooInBar2 = new TestInheritanceMemberItem( +// lineNumber: 14, +// memberName: "Overrides Sub Bar2.Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Sub IBar.Foo()", +// locationTag: "target6", +// relationship: InheritanceRelationship.Implementing), +// new TargetInfo( +// targetSymbolDisplayName: "Overridable Sub Bar1.Foo()", +// locationTag: "target2", +// relationship: InheritanceRelationship.Overriding))); + +// return VerifyInSingleDocumentAsync( +// testDuplicate ? markup2 : markup1, +// LanguageNames.VisualBasic, +// itemForIBar, +// itemForFooInIBar, +// itemForBar1, +// itemForFooInBar1, +// itemForBar2, +// itemForFooInBar2); +// } + +// [Fact] +// public Task TestVisualBasicFindGenericsBaseType() +// { +// var markup = @" +//Public Interface {|target5:IBar|}(Of T) +// Sub {|target6:Foo|}() +//End Interface + +//Public Class {|target1:Bar|} +// Implements IBar(Of Integer) +// Implements IBar(Of String) + +// Public Sub {|target3:Foo|}() Implements IBar(Of Integer).Foo +// Throw New NotImplementedException() +// End Sub + +// Private Sub {|target4:IBar_Foo|}() Implements IBar(Of String).Foo +// Throw New NotImplementedException() +// End Sub +//End Class"; + +// var itemForIBar = new TestInheritanceMemberItem( +// lineNumber: 2, +// memberName: "Interface IBar(Of T)", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Class Bar", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForFooInIBar = new TestInheritanceMemberItem( +// lineNumber: 3, +// memberName: "Sub IBar(Of T).Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Sub Bar.Foo()", +// locationTag: "target3", +// relationship: InheritanceRelationship.Implemented), +// new TargetInfo( +// targetSymbolDisplayName: "Sub Bar.IBar_Foo()", +// locationTag: "target4", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForBar = new TestInheritanceMemberItem( +// lineNumber: 6, +// memberName: "Class Bar", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Interface IBar(Of T)", +// locationTag: "target5", +// relationship: InheritanceRelationship.Implementing))); + +// var itemForFooInBar = new TestInheritanceMemberItem( +// lineNumber: 10, +// memberName: "Sub Bar.Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Sub IBar(Of T).Foo()", +// locationTag: "target6", +// relationship: InheritanceRelationship.Implementing))); + +// var itemForIBar_FooInBar = new TestInheritanceMemberItem( +// lineNumber: 14, +// memberName: "Sub Bar.IBar_Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Sub IBar(Of T).Foo()", +// locationTag: "target6", +// relationship: InheritanceRelationship.Implementing))); + +// return VerifyInSingleDocumentAsync( +// markup, +// LanguageNames.VisualBasic, +// itemForIBar, +// itemForFooInIBar, +// itemForBar, +// itemForFooInBar, +// itemForIBar_FooInBar); +// } + +// #endregion + +// [Fact] +// public Task TestCSharpProjectReferencingVisualBasicProject() +// { +// var markup1 = @" +//using MyNamespace; +//namespace BarNs +//{ +// public class {|target2:Bar|} : IBar +// { +// public void {|target4:Foo|}() { } +// } +//}"; + +// var markup2 = @" +//Namespace MyNamespace +// Public Interface {|target1:IBar|} +// Sub {|target3:Foo|}() +// End Interface +//End Namespace"; + +// var itemForBar = new TestInheritanceMemberItem( +// lineNumber: 5, +// memberName: "class Bar", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Interface IBar", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implementing))); + +// var itemForFooInMarkup1 = new TestInheritanceMemberItem( +// lineNumber: 7, +// memberName: "void Bar.Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Sub IBar.Foo()", +// locationTag: "target3", +// relationship: InheritanceRelationship.Implementing))); + +// var itemForIBar = new TestInheritanceMemberItem( +// lineNumber: 3, +// memberName: "Interface IBar", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "class Bar", +// locationTag: "target2", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForFooInMarkup2 = new TestInheritanceMemberItem( +// lineNumber: 4, +// memberName: "Sub IBar.Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "void Bar.Foo()", +// locationTag: "target4", +// relationship: InheritanceRelationship.Implemented))); + +// return VerifyInDifferentProjectsAsync( +// (markup1, LanguageNames.CSharp), +// (markup2, LanguageNames.VisualBasic), +// new[] { itemForBar, itemForFooInMarkup1 }, +// new[] { itemForIBar, itemForFooInMarkup2 }); +// } + +// [Fact] +// public Task TestVisualBasicProjectReferencingCSharpProject() +// { +// var markup1 = @" +//Imports BarNs +//Namespace MyNamespace +// Public Class {|target2:Bar44|} +// Implements IBar + +// Public Sub {|target4:Foo|}() Implements IBar.Foo +// End Sub +// End Class +//End Namespace"; + +// var markup2 = @" +//namespace BarNs +//{ +// public interface {|target1:IBar|} +// { +// void {|target3:Foo|}(); +// } +//}"; + +// var itemForBar44 = new TestInheritanceMemberItem( +// lineNumber: 4, +// memberName: "Class Bar44", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "interface IBar", +// locationTag: "target1", +// relationship: InheritanceRelationship.Implementing))); + +// var itemForFooInMarkup1 = new TestInheritanceMemberItem( +// lineNumber: 7, +// memberName: "Sub Bar44.Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "void IBar.Foo()", +// locationTag: "target3", +// relationship: InheritanceRelationship.Implementing))); + +// var itemForIBar = new TestInheritanceMemberItem( +// lineNumber: 4, +// memberName: "interface IBar", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Class Bar44", +// locationTag: "target2", +// relationship: InheritanceRelationship.Implemented))); + +// var itemForFooInMarkup2 = new TestInheritanceMemberItem( +// lineNumber: 6, +// memberName: "void IBar.Foo()", +// targets: ImmutableArray.Create(new TargetInfo( +// targetSymbolDisplayName: "Sub Bar44.Foo()", +// locationTag: "target4", +// relationship: InheritanceRelationship.Implemented))); + +// return VerifyInDifferentProjectsAsync( +// (markup1, LanguageNames.VisualBasic), +// (markup2, LanguageNames.CSharp), +// new[] { itemForBar44, itemForFooInMarkup1 }, +// new[] { itemForIBar, itemForFooInMarkup2 }); +// } +// } +//} diff --git a/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs b/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs index 8e65810316bb0..ae07fc0a618a9 100644 --- a/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs +++ b/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs @@ -17,51 +17,21 @@ internal enum InheritanceRelationship /// None = 0, - /// - /// Indicate the target is implementing the member. It would be shown as I↑. - /// - Implementing = 1, - - /// - /// Indicate the target is implemented by the member. It would be shown as I↓. - /// - Implemented = 2, - - /// - /// Indicate the target is overriding the member. It would be shown as O↑. - /// - Overriding = 4, - - /// - /// Indicate the target is overridden by the member. It would be shown as O↓. - /// - Overridden = 8, - - /// - /// A compound value for indicating there are multiple targets both implementing and overriding the member. - /// - ImplementingOverriding = InheritanceRelationship.Implementing | InheritanceRelationship.Overriding, - - /// - /// A compound value for indicating there are multiple targets both implementing the member and overriden by the member. - /// - ImplementingOverridden = InheritanceRelationship.Implementing | InheritanceRelationship.Overridden, - // class & struct - ImplementedInterface, - BaseType, - DerivedType, + ImplementedInterface = 1, + BaseType = 2, + DerivedType = 4, // interface - InheritedInterface, - ImplementingType, + InheritedInterface = 8, + ImplementingType = 16, // class & structure members - ImplmentedMember, - OverriddenMember, - OverridingMember, + ImplmentedMember = 32, + OverriddenMember = 64, + OverridingMember = 128, // member of interface - ImplementingMember + ImplementingMember = 256 } } diff --git a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs index 34a15b3a44a30..a5278c436a8df 100644 --- a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs +++ b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs @@ -16,87 +16,80 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.InheritanceMarg { internal static class InheritanceMarginHelpers { + private static readonly ImmutableHashSet s_relationshipsShownAs_I_UpArrow + = ImmutableHashSet.Empty + .Add(InheritanceRelationship.ImplementedInterface) + .Add(InheritanceRelationship.InheritedInterface) + .Add(InheritanceRelationship.ImplmentedMember); + + private static readonly ImmutableHashSet s_relationshipsShownAs_I_DownArrow + = ImmutableHashSet.Empty + .Add(InheritanceRelationship.ImplementingType) + .Add(InheritanceRelationship.ImplementingMember); + + private static readonly ImmutableHashSet s_relationshipsShownAs_O_UpArrow + = ImmutableHashSet.Empty + .Add(InheritanceRelationship.BaseType) + .Add(InheritanceRelationship.OverriddenMember); + + private static readonly ImmutableHashSet s_relationshipsShownAs_O_DownArrow + = ImmutableHashSet.Empty + .Add(InheritanceRelationship.DerivedType) + .Add(InheritanceRelationship.OverridingMember); + /// /// Decide which moniker should be shown. /// public static ImageMoniker GetMoniker(InheritanceRelationship inheritanceRelationship) { // If there are multiple targets and we have the corresponding compound image, use it - if (inheritanceRelationship.HasFlag(InheritanceRelationship.ImplementingOverriding)) + if (s_relationshipsShownAs_I_UpArrow.Any(flag => inheritanceRelationship.HasFlag(flag)) + && s_relationshipsShownAs_O_DownArrow.Any(flag => inheritanceRelationship.HasFlag(flag))) { - return KnownMonikers.ImplementingOverriding; + return KnownMonikers.ImplementingOverridden; } - if (inheritanceRelationship.HasFlag(InheritanceRelationship.ImplementingOverridden)) + if (s_relationshipsShownAs_I_UpArrow.Any(flag => inheritanceRelationship.HasFlag(flag)) + && s_relationshipsShownAs_O_UpArrow.Any(flag => inheritanceRelationship.HasFlag(flag))) { - return KnownMonikers.ImplementingOverridden; + return KnownMonikers.ImplementingOverriding; } // Otherwise, show the image based on this preference - if (inheritanceRelationship.HasFlag(InheritanceRelationship.Implemented)) + if (s_relationshipsShownAs_I_UpArrow.Any(flag => inheritanceRelationship.HasFlag(flag))) { - return KnownMonikers.Implemented; + return KnownMonikers.Implementing; } - if (inheritanceRelationship.HasFlag(InheritanceRelationship.Implementing)) + if (s_relationshipsShownAs_I_DownArrow.Any(flag => inheritanceRelationship.HasFlag(flag))) { - return KnownMonikers.Implementing; + return KnownMonikers.Implemented; } - if (inheritanceRelationship.HasFlag(InheritanceRelationship.Overridden)) + if (s_relationshipsShownAs_O_UpArrow.Any(flag => inheritanceRelationship.HasFlag(flag))) { - return KnownMonikers.Overridden; + return KnownMonikers.Overriding; } - if (inheritanceRelationship.HasFlag(InheritanceRelationship.Overriding)) + if (s_relationshipsShownAs_O_DownArrow.Any(flag => inheritanceRelationship.HasFlag(flag))) { - return KnownMonikers.Overriding; + return KnownMonikers.Overridden; } // The relationship is None. Don't know what image should be shown, throws throw ExceptionUtilities.UnexpectedValue(inheritanceRelationship); } - /// - /// Create the view models for the inheritance targets of a single member. - /// There are two cases: - /// 1. If all the targets have the same inheritance relationship. It would be an array of TargetViewModel - /// e.g. - /// Target1ViewModel - /// Target2ViewModel - /// Target3ViewModel - /// - /// 2. If targets belongs to different inheritance group. It would be grouped. - /// e.g. - /// Header1ViewModel - /// Target1ViewModel - /// Target2ViewModel - /// Header2ViewModel - /// Target1ViewModel - /// Target2ViewModel - /// public static ImmutableArray CreateMenuItemViewModelsForSingleMember(ImmutableArray targets) { - var targetsByRelationship = targets.OrderBy(target => target.DisplayName).GroupBy(target => target.RelationToMember) + var targetsByRelationship = targets + .OrderBy(target => target.DisplayName) + .GroupBy(target => target.RelationToMember) .ToImmutableDictionary( keySelector: grouping => grouping.Key, elementSelector: grouping => grouping); - if (targetsByRelationship.Count == 1) - { - // If all targets have one relationship. - // e.g. interface IBar { void Bar(); } - // class A : IBar { void Bar() {} } - // class B : IBar { void Bar() {} } - // for 'IBar', the margin would be I↓. So header is not needed. - var (_, targetItems) = targetsByRelationship.Single(); - return targetItems.SelectAsArray(target => TargetMenuItemViewModel.Create(target, indent: false)).CastArray(); - } - else - { - // Otherwise, it means these targets has different relationship, - // these targets would be shown in group, and a header should be shown as the first item to indicate the relationship to user. - return targetsByRelationship.SelectMany(kvp => CreateMenuItemsWithHeader(kvp.Key, kvp.Value)).ToImmutableArray(); - } + + return targetsByRelationship.SelectMany(kvp => CreateMenuItemsWithHeader(kvp.Key, kvp.Value)).ToImmutableArray(); } /// @@ -126,17 +119,7 @@ public static ImmutableArray CreateMenuItemViewMod // For multiple members, check if all the targets have the same inheritance relationship. // If so, then don't add the header, because it is already indicated by the margin. // Otherwise, add the Header. - var set = members - .SelectMany(member => member.TargetItems.Select(item => item.RelationToMember)) - .ToImmutableHashSet(); - if (set.Count == 1) - { - return members.SelectAsArray(MemberMenuItemViewModel.CreateWithNoHeaderInTargets).CastArray(); - } - else - { - return members.SelectAsArray(MemberMenuItemViewModel.CreateWithHeaderInTargets).CastArray(); - } + return members.SelectAsArray(MemberMenuItemViewModel.CreateWithHeaderInTargets).CastArray(); } public static ImmutableArray CreateMenuItemsWithHeader( @@ -146,10 +129,15 @@ public static ImmutableArray CreateMenuItemsWithHe using var _ = CodeAnalysis.PooledObjects.ArrayBuilder.GetInstance(out var builder); var displayContent = relationship switch { - InheritanceRelationship.Implemented => ServicesVSResources.Implemented_members, - InheritanceRelationship.Implementing => ServicesVSResources.Implementing_members, - InheritanceRelationship.Overriding => ServicesVSResources.Overriding_members, - InheritanceRelationship.Overridden => ServicesVSResources.Overridden_members, + InheritanceRelationship.ImplementedInterface => ServicesVSResources.Implemented_interfaces, + InheritanceRelationship.BaseType => ServicesVSResources.Base_Types, + InheritanceRelationship.DerivedType => ServicesVSResources.Derived_types, + InheritanceRelationship.InheritedInterface => ServicesVSResources.Inherited_interfaces, + InheritanceRelationship.ImplementingType => ServicesVSResources.Implementing_types, + InheritanceRelationship.ImplmentedMember => ServicesVSResources.Implemented_members, + InheritanceRelationship.OverriddenMember => ServicesVSResources.Overridden_members, + InheritanceRelationship.OverridingMember => ServicesVSResources.Overriding_members, + InheritanceRelationship.ImplementingMember => ServicesVSResources.Implementing_members, _ => throw ExceptionUtilities.UnexpectedValue(relationship) }; diff --git a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/MemberMenuItemViewModel.cs b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/MemberMenuItemViewModel.cs index b3c55621dda71..49186e2bb773f 100644 --- a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/MemberMenuItemViewModel.cs +++ b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/MemberMenuItemViewModel.cs @@ -41,19 +41,6 @@ public MemberMenuItemViewModel( Targets = targets; } - public static MemberMenuItemViewModel CreateWithNoHeaderInTargets(InheritanceMarginItem member) - { - var displayName = member.DisplayTexts.JoinText(); - return new MemberMenuItemViewModel( - displayName, - member.Glyph.GetImageMoniker(), - displayName, - member.TargetItems - .OrderBy(item => item.DisplayName) - .SelectAsArray(item => TargetMenuItemViewModel.Create(item, indent: false)) - .CastArray()); - } - public static MemberMenuItemViewModel CreateWithHeaderInTargets(InheritanceMarginItem member) { var displayName = member.DisplayTexts.JoinText(); diff --git a/src/VisualStudio/Core/Def/ServicesVSResources.resx b/src/VisualStudio/Core/Def/ServicesVSResources.resx index cf374e9343835..bc7729e2f1078 100644 --- a/src/VisualStudio/Core/Def/ServicesVSResources.resx +++ b/src/VisualStudio/Core/Def/ServicesVSResources.resx @@ -1747,4 +1747,16 @@ I agree to all of the foregoing: Overridden members + + Derived types + + + Implemented interfaces + + + Implementing types + + + Inherited interfaces + \ No newline at end of file diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf index 2002c08bc6807..8e0780658ac58 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf @@ -242,6 +242,11 @@ Aktuální parametr + + Derived types + Derived types + + Disabled Zakázáno @@ -377,6 +382,11 @@ ID + + Implemented interfaces + Implemented interfaces + + Implemented members Implementovaní členové @@ -387,6 +397,11 @@ Implementace členů + + Implementing types + Implementing types + + In other operators V jiných operátorech @@ -417,6 +432,11 @@ Míra dědičnosti (experimentální) + + Inherited interfaces + Inherited interfaces + + Inline Hints (experimental) Vložené nápovědy (experimentální) diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf index 301bfcadbfd8d..9e79dbb47f400 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf @@ -242,6 +242,11 @@ Aktueller Parameter + + Derived types + Derived types + + Disabled Deaktiviert @@ -377,6 +382,11 @@ ID + + Implemented interfaces + Implemented interfaces + + Implemented members Implementierte Member @@ -387,6 +397,11 @@ Member werden implementiert. + + Implementing types + Implementing types + + In other operators In anderen Operatoren @@ -417,6 +432,11 @@ Vererbungsrand (experimentell) + + Inherited interfaces + Inherited interfaces + + Inline Hints (experimental) Inlinehinweise (experimentell) diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf index 0c45d7110260e..639cd8ffff924 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf @@ -242,6 +242,11 @@ Parámetro actual + + Derived types + Derived types + + Disabled Deshabilitado @@ -377,6 +382,11 @@ Id. + + Implemented interfaces + Implemented interfaces + + Implemented members Miembros implementados @@ -387,6 +397,11 @@ Implementando miembros + + Implementing types + Implementing types + + In other operators En otros operadores @@ -417,6 +432,11 @@ Margen de herencia (experimental) + + Inherited interfaces + Inherited interfaces + + Inline Hints (experimental) Sugerencias en línea (experimental) diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf index 1f65b3053e75c..2b09daff7daa5 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf @@ -242,6 +242,11 @@ Paramètre actuel + + Derived types + Derived types + + Disabled Désactivé @@ -377,6 +382,11 @@ ID + + Implemented interfaces + Implemented interfaces + + Implemented members Membres implémentés @@ -387,6 +397,11 @@ Implémentation des membres + + Implementing types + Implementing types + + In other operators Dans les autres opérateurs @@ -417,6 +432,11 @@ Marge d’héritage (expérimental) + + Inherited interfaces + Inherited interfaces + + Inline Hints (experimental) Indicateurs inline (expérimental) diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf index 0349965882bbd..e4128b54f131b 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf @@ -242,6 +242,11 @@ Parametro corrente + + Derived types + Derived types + + Disabled Disabilitato @@ -377,6 +382,11 @@ ID + + Implemented interfaces + Implemented interfaces + + Implemented members Membri implementati @@ -387,6 +397,11 @@ Membri di implementazione + + Implementing types + Implementing types + + In other operators In altri operatori @@ -417,6 +432,11 @@ Margine ereditarietà (sperimentale) + + Inherited interfaces + Inherited interfaces + + Inline Hints (experimental) Suggerimenti inline (sperimentale) diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf index 23f9994b62188..ae8edb641f274 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf @@ -242,6 +242,11 @@ 現在のパラメーター + + Derived types + Derived types + + Disabled 無効 @@ -377,6 +382,11 @@ ID + + Implemented interfaces + Implemented interfaces + + Implemented members 実装されたメンバー @@ -387,6 +397,11 @@ メンバーを実装中 + + Implementing types + Implementing types + + In other operators その他の演算子内で @@ -417,6 +432,11 @@ 継承の余白 (試験段階) + + Inherited interfaces + Inherited interfaces + + Inline Hints (experimental) インラインのヒント (試験段階) diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf index ca4cc9445efba..ba22961a6f88d 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf @@ -242,6 +242,11 @@ 현재 매개 변수 + + Derived types + Derived types + + Disabled 사용 안 함 @@ -377,6 +382,11 @@ ID + + Implemented interfaces + Implemented interfaces + + Implemented members 구현된 구성원 @@ -387,6 +397,11 @@ 구성원을 구현하는 중 + + Implementing types + Implementing types + + In other operators 기타 연산자 @@ -417,6 +432,11 @@ 상속 여백(실험용) + + Inherited interfaces + Inherited interfaces + + Inline Hints (experimental) 인라인 힌트(실험적) diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf index aeb7bee13c4ad..30a786621ed51 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf @@ -242,6 +242,11 @@ Bieżący parametr + + Derived types + Derived types + + Disabled Wyłączone @@ -377,6 +382,11 @@ Identyfikator + + Implemented interfaces + Implemented interfaces + + Implemented members Zaimplementowane składowe @@ -387,6 +397,11 @@ Implementowanie składowych + + Implementing types + Implementing types + + In other operators W innych operatorach @@ -417,6 +432,11 @@ Margines dziedziczenia (eksperymentalny) + + Inherited interfaces + Inherited interfaces + + Inline Hints (experimental) Wskazówki w tekście (eksperymentalne) diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf index f62f1065ba862..d276b432ec082 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf @@ -242,6 +242,11 @@ Parâmetro atual + + Derived types + Derived types + + Disabled Desabilitado @@ -377,6 +382,11 @@ ID + + Implemented interfaces + Implemented interfaces + + Implemented members Membros implementados @@ -387,6 +397,11 @@ Implementando membros + + Implementing types + Implementing types + + In other operators Em outros operadores @@ -417,6 +432,11 @@ Margem de Herança (experimental) + + Inherited interfaces + Inherited interfaces + + Inline Hints (experimental) Dicas Embutidas (experimental) diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf index c7e53a7d54069..3a279252d7a13 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf @@ -242,6 +242,11 @@ Текущий параметр + + Derived types + Derived types + + Disabled Отключено @@ -377,6 +382,11 @@ ИД + + Implemented interfaces + Implemented interfaces + + Implemented members Реализованные элементы @@ -387,6 +397,11 @@ Реализация элементов + + Implementing types + Implementing types + + In other operators В других операторах @@ -417,6 +432,11 @@ Граница наследования (экспериментальная) + + Inherited interfaces + Inherited interfaces + + Inline Hints (experimental) Встроенные подсказки (экспериментальная функция) diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf index f6e7d482a1cdd..e1d4d8fe4bc0a 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf @@ -242,6 +242,11 @@ Geçerli parametre + + Derived types + Derived types + + Disabled Devre dışı @@ -377,6 +382,11 @@ Kimlik + + Implemented interfaces + Implemented interfaces + + Implemented members Uygulanan üyeler @@ -387,6 +397,11 @@ Üyeleri uygulama + + Implementing types + Implementing types + + In other operators Diğer işleçlerde @@ -417,6 +432,11 @@ Devralma Marjı (deneysel) + + Inherited interfaces + Inherited interfaces + + Inline Hints (experimental) Satır İçi İpuçları (deneysel) diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf index 793a652ff626f..affa90b46aa23 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf @@ -242,6 +242,11 @@ 当前参数 + + Derived types + Derived types + + Disabled 已禁用 @@ -377,6 +382,11 @@ ID + + Implemented interfaces + Implemented interfaces + + Implemented members 实现的成员 @@ -387,6 +397,11 @@ 正在实现成员 + + Implementing types + Implementing types + + In other operators 在其他运算符中 @@ -417,6 +432,11 @@ 继承边距(实验性) + + Inherited interfaces + Inherited interfaces + + Inline Hints (experimental) 内联提示(实验性) diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf index 3da8a86c54954..c9aa9c0bb156e 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf @@ -242,6 +242,11 @@ 目前的參數 + + Derived types + Derived types + + Disabled 已停用 @@ -377,6 +382,11 @@ 識別碼 + + Implemented interfaces + Implemented interfaces + + Implemented members 已實作的成員 @@ -387,6 +397,11 @@ 實作成員 + + Implementing types + Implementing types + + In other operators 其他運算子中 @@ -417,6 +432,11 @@ 繼承邊界 (實驗性) + + Inherited interfaces + Inherited interfaces + + Inline Hints (experimental) 內嵌提示 (實驗性) From 82c29399da919a04fc51ce1d48226358ca270150 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Tue, 8 Jun 2021 17:04:12 -0700 Subject: [PATCH 097/127] Code clean up --- .../InheritanceMarginServiceHelpers.cs | 271 +++++------------- .../InheritanceRelationship.cs | 34 ++- .../MarginGlyph/HeaderMenuItemViewModel.cs | 6 +- 3 files changed, 96 insertions(+), 215 deletions(-) diff --git a/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs b/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs index 69c33d14e84f1..da42c55125b89 100644 --- a/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs +++ b/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs @@ -10,12 +10,9 @@ using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.FindSymbols.FindReferences; using Microsoft.CodeAnalysis.FindUsages; -using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.InheritanceMargin { @@ -90,7 +87,7 @@ private static async ValueTask if (symbol is IEventSymbol or IPropertySymbol or IMethodSymbol) { - await AddInheritanceMemberItemsForClassAndStructMembersAsync(solution, symbol, lineNumber, builder, cancellationToken).ConfigureAwait(false); + await AddInheritanceMemberItemsForMembersAsync(solution, symbol, lineNumber, builder, cancellationToken).ConfigureAwait(false); } } @@ -144,7 +141,7 @@ private static async ValueTask AddInheritanceMemberItemsForNamedTypeAsync( } else { - var item = await CreateInheritanceMemberItemForClassAndStructureAsync( + var item = await CreateInheritanceItemForClassAndStructureAsync( solution, memberSymbol, lineNumber, @@ -156,69 +153,63 @@ private static async ValueTask AddInheritanceMemberItemsForNamedTypeAsync( } } - private static async ValueTask AddInheritanceMemberItemsForClassAndStructMembersAsync( + private static async ValueTask AddInheritanceMemberItemsForMembersAsync( Solution solution, ISymbol memberSymbol, int lineNumber, ArrayBuilder builder, CancellationToken cancellationToken) { - // For a given member symbol (method, property and event), its base and derived symbols are classified into 4 cases. - // The mapping between images - // Implemented : I↓ - // Implementing : I↑ - // Overridden: O↓ - // Overriding: O↑ - - // Go down the inheritance chain to find all the overrides targets. - var allOverriddenSymbols = await SymbolFinder.FindOverridesArrayAsync(memberSymbol, solution, cancellationToken: cancellationToken).ConfigureAwait(false); - - // Go up the inheritance chain to find all overriding targets - var overridingSymbols = GetOverridingSymbols(memberSymbol); - - // Go up the inheritance chain to find all the implemented targets. - var implementingSymbols = GetImplementingSymbolsForTypeMember(memberSymbol, overridingSymbols); - - // Go down the inheritance chain to find all the implementing targets. - var allImplementedSymbols = await GetImplementedSymbolsForTypeMemberAsync(solution, memberSymbol, cancellationToken).ConfigureAwait(false); + if (memberSymbol.ContainingSymbol.IsInterfaceType()) + { + // Go down the inheritance chain to find all the implementing targets. + var allImplementingSymbols = await GetImplementingSymbolsForTypeMemberAsync(solution, memberSymbol, cancellationToken).ConfigureAwait(false); - // For all overriden & implemented symbols, make sure it is in source. - // For example, if the user is viewing System.Threading.SynchronizationContext from metadata, - // then don't show the derived overriden & implemented method in the default implementation for System.Threading.SynchronizationContext in metadata - var overriddenSymbols = allOverriddenSymbols.WhereAsArray(symbol => symbol.Locations.Any(l => l.IsInSource)); - var implementedSymbols = allImplementedSymbols.WhereAsArray(symbol => symbol.Locations.Any(l => l.IsInSource)); + // For all implementing symbols, make sure it is in source. + // For example, if the user is viewing IEnumerable from metadata, + // then don't show the derived overriden & implemented types in System.Collections + var implementingSymbols = allImplementingSymbols.WhereAsArray(symbol => symbol.Locations.Any(l => l.IsInSource)); - if (overriddenSymbols.Any() || overridingSymbols.Any() || implementingSymbols.Any() || implementedSymbols.Any()) - { - if (memberSymbol.ContainingSymbol.IsInterfaceType()) + if (implementingSymbols.Any()) { - var item = await CreateInheritanceMemberInfoForInterfaceMemberAsync(solution, + var item = await CreateInheritanceMemberItemForInterfaceMemberAsync(solution, memberSymbol, lineNumber, - implementedMembers: implementedSymbols, - overridenMembers: overriddenSymbols, - overridingMembers: overridingSymbols, + implementingMembers: implementingSymbols, cancellationToken).ConfigureAwait(false); - builder.AddIfNotNull(item); } - else + } + else + { + // Go down the inheritance chain to find all the overriding targets. + var allOverridingSymbols = await SymbolFinder.FindOverridesArrayAsync(memberSymbol, solution, cancellationToken: cancellationToken).ConfigureAwait(false); + + // Go up the inheritance chain to find all overridden targets + var overriddenSymbols = GetOverriddenSymbols(memberSymbol); + + // Go up the inheritance chain to find all the implemented targets. + var implementedSymbols = GetImplementedSymbolsForTypeMember(memberSymbol, overriddenSymbols); + + // For all overriding symbols, make sure it is in source. + // For example, if the user is viewing System.Threading.SynchronizationContext from metadata, + // then don't show the derived overriden & implemented method in the default implementation for System.Threading.SynchronizationContext in metadata + var overridingSymbols = allOverridingSymbols.WhereAsArray(symbol => symbol.Locations.Any(l => l.IsInSource)); + + if (overridingSymbols.Any() || overriddenSymbols.Any() || implementedSymbols.Any()) { - var item = await CreateInheritanceMemberInfoForClassOrStructMemberAsync(solution, + var item = await CreateInheritanceMemberItemForClassOrStructMemberAsync(solution, memberSymbol, lineNumber, - implementingMembers: implementingSymbols, - overridenMembers: overriddenSymbols, + implementedMembers: implementedSymbols, overridingMembers: overridingSymbols, + overriddenMembers: overriddenSymbols, cancellationToken).ConfigureAwait(false); - builder.AddIfNotNull(item); } } } - #region Interface - private static async ValueTask CreateInheritanceMemberItemForInterfaceAsync( Solution solution, INamedTypeSymbol interfaceSymbol, @@ -254,16 +245,14 @@ private static async ValueTask CreateInherita baseSymbolItems.Concat(derivedTypeItems)); } - private static async ValueTask CreateInheritanceMemberInfoForInterfaceMemberAsync( + private static async ValueTask CreateInheritanceMemberItemForInterfaceMemberAsync( Solution solution, ISymbol memberSymbol, int lineNumber, - ImmutableArray implementedMembers, - ImmutableArray overridenMembers, - ImmutableArray overridingMembers, + ImmutableArray implementingMembers, CancellationToken cancellationToken) { - var implementedMemberItems = await implementedMembers + var implementedMemberItems = await implementingMembers .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( @@ -272,37 +261,14 @@ private static async ValueTask CreateInherita InheritanceRelationship.ImplementingMember, cancellationToken), cancellationToken).ConfigureAwait(false); - var overridenMemberItems = await overridenMembers - .SelectAsArray(symbol => symbol.OriginalDefinition) - .Distinct() - .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( - solution, - symbol, - InheritanceRelationship.OverriddenMember, - cancellationToken), cancellationToken).ConfigureAwait(false); - - var overridingMemberItems = await overridingMembers - .SelectAsArray(symbol => symbol.OriginalDefinition) - .Distinct() - .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( - solution, - symbol, - InheritanceRelationship.OverridingMember, - cancellationToken), cancellationToken).ConfigureAwait(false); - return new SerializableInheritanceMarginItem( lineNumber, FindUsagesHelpers.GetDisplayParts(memberSymbol), memberSymbol.GetGlyph(), - implementedMemberItems - .Concat(overridenMemberItems) - .Concat(overridingMemberItems)); + implementedMemberItems); } - #endregion - - #region ClassAndStruct - private static async ValueTask CreateInheritanceMemberItemForClassAndStructureAsync( + private static async ValueTask CreateInheritanceItemForClassAndStructureAsync( Solution solution, INamedTypeSymbol memberSymbol, int lineNumber, @@ -310,26 +276,21 @@ private static async ValueTask CreateInherita ImmutableArray derivedTypesSymbols, CancellationToken cancellationToken) { + // If the target is an interface, it would be shown as 'Inherited interface', + // and if it is an class/struct, it whould be shown as 'Base Type' var baseSymbolItems = await baseSymbols .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() .SelectAsArrayAsync((symbol, _) => - { - if (symbol.IsInterfaceType()) - { - return CreateInheritanceItemAsync( + symbol.IsInterfaceType() + ? CreateInheritanceItemAsync( solution, symbol, - InheritanceRelationship.InheritedInterface, cancellationToken); - } - else - { - return CreateInheritanceItemAsync( + InheritanceRelationship.InheritedInterface, cancellationToken) + : CreateInheritanceItemAsync( solution, symbol, - InheritanceRelationship.BaseType, cancellationToken); - } - }, cancellationToken) + InheritanceRelationship.BaseType, cancellationToken), cancellationToken) .ConfigureAwait(false); var derivedTypeItems = await derivedTypesSymbols @@ -349,16 +310,16 @@ private static async ValueTask CreateInherita baseSymbolItems.Concat(derivedTypeItems)); } - private static async ValueTask CreateInheritanceMemberInfoForClassOrStructMemberAsync( + private static async ValueTask CreateInheritanceMemberItemForClassOrStructMemberAsync( Solution solution, ISymbol memberSymbol, int lineNumber, - ImmutableArray implementingMembers, - ImmutableArray overridenMembers, + ImmutableArray implementedMembers, ImmutableArray overridingMembers, + ImmutableArray overriddenMembers, CancellationToken cancellationToken) { - var implementingMemberItems = await implementingMembers + var implementedMemberItems = await implementedMembers .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( @@ -367,7 +328,7 @@ private static async ValueTask CreateInherita InheritanceRelationship.ImplmentedMember, cancellationToken), cancellationToken).ConfigureAwait(false); - var overridenMemberItems = await overridenMembers + var overridenMemberItems = await overriddenMembers .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( @@ -389,45 +350,10 @@ private static async ValueTask CreateInherita lineNumber, FindUsagesHelpers.GetDisplayParts(memberSymbol), memberSymbol.GetGlyph(), - implementingMemberItems + implementedMemberItems .Concat(overridenMemberItems) .Concat(overridingMemberItems)); } - #endregion - //private static async ValueTask CreateInheritanceMemberItemForNamedTypeAsync( - // Solution solution, - // INamedTypeSymbol memberSymbol, - // int lineNumber, - // ImmutableArray baseSymbols, - // ImmutableArray derivedTypesSymbols, - // CancellationToken cancellationToken) - //{ - // var baseSymbolItems = await baseSymbols - // .SelectAsArray(symbol => symbol.OriginalDefinition) - // .Distinct() - // .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( - // solution, - // symbol, - // InheritanceRelationship.Implementing, - // cancellationToken), cancellationToken) - // .ConfigureAwait(false); - - // var derivedTypeItems = await derivedTypesSymbols - // .SelectAsArray(symbol => symbol.OriginalDefinition) - // .Distinct() - // .SelectAsArrayAsync((symbol, _) => - // CreateInheritanceItemAsync(solution, - // symbol, - // InheritanceRelationship.Implemented, - // cancellationToken), cancellationToken) - // .ConfigureAwait(false); - - // return new SerializableInheritanceMarginItem( - // lineNumber, - // FindUsagesHelpers.GetDisplayParts(memberSymbol), - // memberSymbol.GetGlyph(), - // baseSymbolItems.Concat(derivedTypeItems)); - //} private static async ValueTask CreateInheritanceItemAsync( Solution solution, @@ -454,65 +380,9 @@ private static async ValueTask CreateInherita displayName); } - //private static async ValueTask CreateInheritanceMemberInfoForMemberAsync( - // Solution solution, - // ISymbol memberSymbol, - // int lineNumber, - // ImmutableArray implementingMembers, - // ImmutableArray implementedMembers, - // ImmutableArray overridenMembers, - // ImmutableArray overridingMembers, - // CancellationToken cancellationToken) - //{ - // var implementingMemberItems = await implementingMembers - // .SelectAsArray(symbol => symbol.OriginalDefinition) - // .Distinct() - // .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( - // solution, - // symbol, - // InheritanceRelationship.ImplmentedMember, - // cancellationToken), cancellationToken).ConfigureAwait(false); - - // var implementedMemberItems = await implementedMembers - // .SelectAsArray(symbol => symbol.OriginalDefinition) - // .Distinct() - // .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( - // solution, - // symbol, - // InheritanceRelationship.Implemented, - // cancellationToken), cancellationToken).ConfigureAwait(false); - - // var overridenMemberItems = await overridenMembers - // .SelectAsArray(symbol => symbol.OriginalDefinition) - // .Distinct() - // .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( - // solution, - // symbol, - // InheritanceRelationship.OverriddenMember, - // cancellationToken), cancellationToken).ConfigureAwait(false); - - // var overridingMemberItems = await overridingMembers - // .SelectAsArray(symbol => symbol.OriginalDefinition) - // .Distinct() - // .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( - // solution, - // symbol, - // InheritanceRelationship.OverridingMember, - // cancellationToken), cancellationToken).ConfigureAwait(false); - - // return new SerializableInheritanceMarginItem( - // lineNumber, - // FindUsagesHelpers.GetDisplayParts(memberSymbol), - // memberSymbol.GetGlyph(), - // implementingMemberItems.Concat(implementedMemberItems) - // .Concat(overridenMemberItems) - // .Concat(overridingMemberItems)); - //} - - #region FindAllReferenceHelpers - private static ImmutableArray GetImplementingSymbolsForTypeMember( + private static ImmutableArray GetImplementedSymbolsForTypeMember( ISymbol memberSymbol, - ImmutableArray overridingSymbols) + ImmutableArray overriddenSymbols) { if (memberSymbol is IMethodSymbol or IEventSymbol or IPropertySymbol) { @@ -522,13 +392,13 @@ private static ImmutableArray GetImplementingSymbolsForTypeMember( var directImplementingSymbols = memberSymbol.ExplicitOrImplicitInterfaceImplementations(); builder.AddRange(directImplementingSymbols); - // 2. Also add the direct implementing symbols for the overriding symbols. + // 2. Also add the direct implementing symbols for the overridden symbols. // For example: // interface IBar { void Foo(); } // class Bar : IBar { public override void Foo() { } } // class Bar2 : Bar { public override void Foo() { } } // For 'Bar2.Foo()', we need to find 'IBar.Foo()' - foreach (var symbol in overridingSymbols) + foreach (var symbol in overriddenSymbols) { builder.AddRange(symbol.ExplicitOrImplicitInterfaceImplementations()); } @@ -540,14 +410,9 @@ private static ImmutableArray GetImplementingSymbolsForTypeMember( } /// - /// For the , get all the implemented symbols. - /// Table for the mapping between images and inheritanceRelationship - /// Implemented : I↓ - /// Implementing : I↑ - /// Overridden: O↓ - /// Overriding: O↑ + /// For the , get all the implementing symbols. /// - private static async Task> GetImplementedSymbolsForTypeMemberAsync( + private static async Task> GetImplementingSymbolsForTypeMemberAsync( Solution solution, ISymbol memberSymbol, CancellationToken cancellationToken) @@ -580,14 +445,9 @@ private static async Task> GetImplementedSymbolsForTypeM } /// - /// Get members overriding the - /// Table for the mapping between images and inheritanceRelationship - /// Implemented : I↓ - /// Implementing : I↑ - /// Overridden: O↓ - /// Overriding: O↑ + /// Get overridden members the . /// - private static ImmutableArray GetOverridingSymbols(ISymbol memberSymbol) + private static ImmutableArray GetOverriddenSymbols(ISymbol memberSymbol) { if (memberSymbol is INamedTypeSymbol) { @@ -596,11 +456,11 @@ private static ImmutableArray GetOverridingSymbols(ISymbol memberSymbol else { using var _ = ArrayBuilder.GetInstance(out var builder); - for (var overridenMember = memberSymbol.GetOverriddenMember(); - overridenMember != null; - overridenMember = overridenMember.GetOverriddenMember()) + for (var overriddenMember = memberSymbol.GetOverriddenMember(); + overriddenMember != null; + overriddenMember = overriddenMember.GetOverriddenMember()) { - builder.Add(overridenMember.OriginalDefinition); + builder.Add(overriddenMember.OriginalDefinition); } return builder.ToImmutableArray(); @@ -638,6 +498,5 @@ private static async Task> GetDerivedTypesAndIm cancellationToken: cancellationToken).ConfigureAwait(false); } } - #endregion } } diff --git a/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs b/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs index ae07fc0a618a9..9223b234e5ff7 100644 --- a/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs +++ b/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs @@ -17,21 +17,47 @@ internal enum InheritanceRelationship /// None = 0, - // class & struct + /// + /// Implented interfaces for class or struct. Shown as I↑ + /// ImplementedInterface = 1, + + /// + /// Base type for class or struct. Shown as O↑ + /// BaseType = 2, + + /// + /// Derived type for class or struct. Shown as O↓ + /// DerivedType = 4, - // interface + /// + /// Inherited interface for interface. Shown as I↑ + /// InheritedInterface = 8, + + /// + /// Implementing class, struct and interface for interface. Shown as I↓ + /// ImplementingType = 16, - // class & structure members + /// + /// Implemented member for member in class or structure. Shown as I↑ + /// ImplmentedMember = 32, + + /// + /// Overriden member for member in class or structure. Shown as O↑ + /// OverriddenMember = 64, + + /// + /// Overrrding member for member in class or structure. Shown as O↓ + /// OverridingMember = 128, - // member of interface + // Implmenting member for member in interface. Shown as I↓ ImplementingMember = 256 } } diff --git a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/HeaderMenuItemViewModel.cs b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/HeaderMenuItemViewModel.cs index c1cc072ae084f..ae8e6d1a71fd8 100644 --- a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/HeaderMenuItemViewModel.cs +++ b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/HeaderMenuItemViewModel.cs @@ -8,13 +8,9 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.InheritanceMarg { /// /// The view model used for the header of TargetMenuItemViewModel. - /// It is used when the context menu contains targets having multiple inheritance relationship. - /// In such case, this would be shown as a header for a group of targets. /// e.g. - /// 'I↓ Implemented members' + /// 'I↓ Implementing members' /// Method 'Bar' - /// 'I↑ Implementing members' - /// Method 'Foo' /// internal class HeaderMenuItemViewModel : InheritanceMenuItemViewModel { From 7d5c6abf031d0765c3d40248bcb0e036f7d73cbf Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Tue, 8 Jun 2021 21:12:44 -0700 Subject: [PATCH 098/127] Fix InheritanceMargin test --- .../InheritanceMarginServiceHelpers.cs | 4 +- .../InheritanceMarginTests.cs | 3416 ++++++++--------- .../InheritanceRelationship.cs | 2 +- .../InheritanceMarginHelpers.cs | 4 +- 4 files changed, 1702 insertions(+), 1724 deletions(-) diff --git a/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs b/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs index da42c55125b89..660040c34312d 100644 --- a/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs +++ b/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs @@ -286,7 +286,7 @@ private static async ValueTask CreateInherita ? CreateInheritanceItemAsync( solution, symbol, - InheritanceRelationship.InheritedInterface, cancellationToken) + InheritanceRelationship.ImplementedInterface, cancellationToken) : CreateInheritanceItemAsync( solution, symbol, @@ -325,7 +325,7 @@ private static async ValueTask CreateInherita .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( solution, symbol, - InheritanceRelationship.ImplmentedMember, + InheritanceRelationship.ImplementedMember, cancellationToken), cancellationToken).ConfigureAwait(false); var overridenMemberItems = await overriddenMembers diff --git a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs index edfeabd6a9ece..234a5fb46f49b 100644 --- a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs +++ b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs @@ -2,1722 +2,1700 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -//using System.Collections.Immutable; -//using System.Linq; -//using System.Security; -//using System.Threading; -//using System.Threading.Tasks; -//using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; -//using Microsoft.CodeAnalysis.InheritanceMargin; -//using Microsoft.CodeAnalysis.PooledObjects; -//using Microsoft.CodeAnalysis.Shared.Extensions; -//using Microsoft.CodeAnalysis.Test.Utilities; -//using Roslyn.Utilities; -//using Xunit; - -//namespace Microsoft.CodeAnalysis.Editor.UnitTests.InheritanceMargin -//{ -// [Trait(Traits.Feature, Traits.Features.InheritanceMargin)] -// [UseExportProvider] -// public class InheritanceMarginTests -// { -// private const string SearchAreaTag = "SeachTag"; - -// #region Helpers - -// private static Task VerifyNoItemForDocumentAsync(string markup, string languageName) -// => VerifyInSingleDocumentAsync(markup, languageName); - -// private static Task VerifyInSingleDocumentAsync( -// string markup, -// string languageName, -// params TestInheritanceMemberItem[] memberItems) -// { -// var workspaceFile = $@" -// -// -// -// {markup} -// -// -//"; - -// var cancellationToken = CancellationToken.None; - -// using var testWorkspace = TestWorkspace.Create( -// workspaceFile, -// composition: EditorTestCompositions.EditorFeatures); - -// var testHostDocument = testWorkspace.Documents[0]; -// return VerifyTestMemberInDocumentAsync(testWorkspace, testHostDocument, memberItems, cancellationToken); -// } - -// private static async Task VerifyTestMemberInDocumentAsync( -// TestWorkspace testWorkspace, -// TestHostDocument testHostDocument, -// TestInheritanceMemberItem[] memberItems, -// CancellationToken cancellationToken) -// { -// var document = testWorkspace.CurrentSolution.GetRequiredDocument(testHostDocument.Id); -// var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); -// var searchingSpan = root.Span; -// // Look for the search span, if not found, then pass the whole document span to the service. -// if (testHostDocument.AnnotatedSpans.TryGetValue(SearchAreaTag, out var spans) && spans.IsSingle()) -// { -// searchingSpan = spans[0]; -// } - -// var service = document.GetRequiredLanguageService(); -// var actualItems = await service.GetInheritanceMemberItemsAsync( -// document, -// searchingSpan, -// cancellationToken).ConfigureAwait(false); - -// var sortedActualItems = actualItems.OrderBy(item => item.LineNumber).ToImmutableArray(); -// var sortedExpectedItems = memberItems.OrderBy(item => item.LineNumber).ToImmutableArray(); -// Assert.Equal(sortedExpectedItems.Length, sortedActualItems.Length); - -// for (var i = 0; i < sortedActualItems.Length; i++) -// { -// VerifyInheritanceMember(testWorkspace, sortedExpectedItems[i], sortedActualItems[i]); -// } -// } - -// private static void VerifyInheritanceMember(TestWorkspace testWorkspace, TestInheritanceMemberItem expectedItem, InheritanceMarginItem actualItem) -// { -// Assert.Equal(expectedItem.LineNumber, actualItem.LineNumber); -// Assert.Equal(expectedItem.MemberName, actualItem.DisplayTexts.JoinText()); -// Assert.Equal(expectedItem.Targets.Length, actualItem.TargetItems.Length); -// var expectedTargets = expectedItem.Targets -// .Select(info => TestInheritanceTargetItem.Create(info, testWorkspace)) -// .OrderBy(target => target.TargetSymbolName) -// .ToImmutableArray(); -// var sortedActualTargets = actualItem.TargetItems.OrderBy(target => target.DefinitionItem.DisplayParts.JoinText()) -// .ToImmutableArray(); -// for (var i = 0; i < expectedTargets.Length; i++) -// { -// VerifyInheritanceTarget(expectedTargets[i], sortedActualTargets[i]); -// } -// } - -// private static void VerifyInheritanceTarget(TestInheritanceTargetItem expectedTarget, InheritanceTargetItem actualTarget) -// { -// Assert.Equal(expectedTarget.TargetSymbolName, actualTarget.DefinitionItem.DisplayParts.JoinText()); -// Assert.Equal(expectedTarget.RelationshipToMember, actualTarget.RelationToMember); - -// if (expectedTarget.IsInMetadata) -// { -// Assert.True(actualTarget.DefinitionItem.Properties.ContainsKey("MetadataSymbolKey")); -// Assert.True(actualTarget.DefinitionItem.SourceSpans.IsEmpty); -// } -// else -// { -// var actualDocumentSpans = actualTarget.DefinitionItem.SourceSpans.OrderBy(documentSpan => documentSpan.SourceSpan.Start).ToImmutableArray(); -// var expectedDocumentSpans = expectedTarget.DocumentSpans.OrderBy(documentSpan => documentSpan.SourceSpan.Start).ToImmutableArray(); -// Assert.Equal(expectedDocumentSpans.Length, actualDocumentSpans.Length); -// for (var i = 0; i < actualDocumentSpans.Length; i++) -// { -// Assert.Equal(expectedDocumentSpans[i].SourceSpan, actualDocumentSpans[i].SourceSpan); -// Assert.Equal(expectedDocumentSpans[i].Document.FilePath, actualDocumentSpans[i].Document.FilePath); -// } -// } -// } - -// /// -// /// Project of markup1 is referencing project of markup2 -// /// -// private static async Task VerifyInDifferentProjectsAsync( -// (string markupInProject1, string languageName) markup1, -// (string markupInProject2, string languageName) markup2, -// TestInheritanceMemberItem[] memberItemsInMarkup1, -// TestInheritanceMemberItem[] memberItemsInMarkup2) -// { -// var workspaceFile = -// $@" -// -// -// Assembly2 -// -// {markup1.markupInProject1} -// -// -// -// -// {markup2.markupInProject2} -// -// -//"; - -// var cancellationToken = CancellationToken.None; -// using var testWorkspace = TestWorkspace.Create( -// workspaceFile, -// composition: EditorTestCompositions.EditorFeatures); - -// var testHostDocument1 = testWorkspace.Documents.Single(doc => doc.Project.AssemblyName.Equals("Assembly1")); -// var testHostDocument2 = testWorkspace.Documents.Single(doc => doc.Project.AssemblyName.Equals("Assembly2")); -// await VerifyTestMemberInDocumentAsync(testWorkspace, testHostDocument1, memberItemsInMarkup1, cancellationToken).ConfigureAwait(false); -// await VerifyTestMemberInDocumentAsync(testWorkspace, testHostDocument2, memberItemsInMarkup2, cancellationToken).ConfigureAwait(false); -// } - -// private class TestInheritanceMemberItem -// { -// public readonly int LineNumber; -// public readonly string MemberName; -// public readonly ImmutableArray Targets; - -// public TestInheritanceMemberItem( -// int lineNumber, -// string memberName, -// ImmutableArray targets) -// { -// LineNumber = lineNumber; -// MemberName = memberName; -// Targets = targets; -// } -// } - -// private class TargetInfo -// { -// public readonly string TargetSymbolDisplayName; -// public readonly string? LocationTag; -// public readonly InheritanceRelationship Relationship; -// public readonly bool InMetadata; - -// public TargetInfo( -// string targetSymbolDisplayName, -// string locationTag, -// InheritanceRelationship relationship) -// { -// TargetSymbolDisplayName = targetSymbolDisplayName; -// LocationTag = locationTag; -// Relationship = relationship; -// InMetadata = false; -// } - -// public TargetInfo( -// string targetSymbolDisplayName, -// InheritanceRelationship relationship, -// bool inMetadata) -// { -// TargetSymbolDisplayName = targetSymbolDisplayName; -// Relationship = relationship; -// InMetadata = inMetadata; -// LocationTag = null; -// } -// } - -// private class TestInheritanceTargetItem -// { -// public readonly string TargetSymbolName; -// public readonly InheritanceRelationship RelationshipToMember; -// public readonly ImmutableArray DocumentSpans; -// public readonly bool IsInMetadata; - -// public TestInheritanceTargetItem( -// string targetSymbolName, -// InheritanceRelationship relationshipToMember, -// ImmutableArray documentSpans, -// bool isInMetadata) -// { -// TargetSymbolName = targetSymbolName; -// RelationshipToMember = relationshipToMember; -// DocumentSpans = documentSpans; -// IsInMetadata = isInMetadata; -// } - -// public static TestInheritanceTargetItem Create( -// TargetInfo targetInfo, -// TestWorkspace testWorkspace) -// { -// if (targetInfo.InMetadata) -// { -// return new TestInheritanceTargetItem( -// targetInfo.TargetSymbolDisplayName, -// targetInfo.Relationship, -// ImmutableArray.Empty, -// isInMetadata: true); -// } -// else -// { -// using var _ = ArrayBuilder.GetInstance(out var builder); -// // If the target is not in metadata, there must be a location tag to give the span! -// Assert.True(targetInfo.LocationTag != null); -// foreach (var testHostDocument in testWorkspace.Documents) -// { -// if (targetInfo.LocationTag != null) -// { -// var annotatedSpans = testHostDocument.AnnotatedSpans; -// if (annotatedSpans.TryGetValue(targetInfo.LocationTag, out var spans)) -// { -// var document = testWorkspace.CurrentSolution.GetRequiredDocument(testHostDocument.Id); -// builder.AddRange(spans.Select(span => new DocumentSpan(document, span))); -// } -// } -// } - -// return new TestInheritanceTargetItem( -// targetInfo.TargetSymbolDisplayName, -// targetInfo.Relationship, -// builder.ToImmutable(), -// isInMetadata: false); -// } -// } -// } - -// #endregion - -// #region TestsForCSharp - -// [Fact] -// public Task TestCSharpClassWithErrorBaseType() -// { -// var markup = @" -//public class Bar : SomethingUnknown -//{ -//}"; -// return VerifyNoItemForDocumentAsync(markup, LanguageNames.CSharp); -// } - -// [Fact] -// public Task TestCSharpReferencingMetadata() -// { -// var markup = @" -//using System.Collections; -//public class Bar : IEnumerable -//{ -// public IEnumerator GetEnumerator () { return null }; -//}"; -// var itemForBar = new TestInheritanceMemberItem( -// lineNumber: 3, -// memberName: "class Bar", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "interface IEnumerable", -// relationship: InheritanceRelationship.Implementing, -// inMetadata: true))); - -// var itemForGetEnumerator = new TestInheritanceMemberItem( -// lineNumber: 5, -// memberName: "IEnumerator Bar.GetEnumerator()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "IEnumerator IEnumerable.GetEnumerator()", -// relationship: InheritanceRelationship.Implementing, -// inMetadata: true))); - -// return VerifyInSingleDocumentAsync(markup, LanguageNames.CSharp, itemForBar, itemForGetEnumerator); -// } - -// [Fact] -// public Task TestCSharpClassImplementingInterface() -// { -// var markup = @" -//interface {|target1:IBar|} { } -//public class {|target2:Bar|} : IBar -//{ -//} -// "; - -// var itemOnLine2 = new TestInheritanceMemberItem( -// lineNumber: 2, -// memberName: "interface IBar", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "class Bar", -// locationTag: "target2", -// relationship: InheritanceRelationship.Implemented))); - -// var itemOnLine3 = new TestInheritanceMemberItem( -// lineNumber: 3, -// memberName: "class Bar", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "interface IBar", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implementing))); - -// return VerifyInSingleDocumentAsync( -// markup, -// LanguageNames.CSharp, -// itemOnLine2, -// itemOnLine3); -// } - -// [Fact] -// public Task TestCSharpInterfaceImplementingInterface() -// { -// var markup = @" -//interface {|target1:IBar|} { } -//interface {|target2:IBar2|} : IBar { } -// "; - -// var itemOnLine2 = new TestInheritanceMemberItem( -// lineNumber: 2, -// memberName: "interface IBar", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "interface IBar2", -// locationTag: "target2", -// relationship: InheritanceRelationship.Implemented)) -// ); -// var itemOnLine3 = new TestInheritanceMemberItem( -// lineNumber: 3, -// memberName: "interface IBar2", -// targets: ImmutableArray.Empty -// .Add(new TargetInfo( -// targetSymbolDisplayName: "interface IBar", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implementing)) -// ); - -// return VerifyInSingleDocumentAsync( -// markup, -// LanguageNames.CSharp, -// itemOnLine2, -// itemOnLine3); -// } - -// [Fact] -// public Task TestCSharpClassInheritsClass() -// { -// var markup = @" -//class {|target2:A|} { } -//class {|target1:B|} : A { } -// "; - -// var itemOnLine2 = new TestInheritanceMemberItem( -// lineNumber: 2, -// memberName: "class A", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "class B", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implemented)) -// ); -// var itemOnLine3 = new TestInheritanceMemberItem( -// lineNumber: 3, -// memberName: "class B", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "class A", -// locationTag: "target2", -// relationship: InheritanceRelationship.Implementing)) -// ); - -// return VerifyInSingleDocumentAsync( -// markup, -// LanguageNames.CSharp, -// itemOnLine2, -// itemOnLine3); -// } - -// [Theory] -// [InlineData("class")] -// [InlineData("struct")] -// [InlineData("enum")] -// [InlineData("interface")] -// public Task TestCSharpTypeWithoutBaseType(string typeName) -// { -// var markup = $@" -//public {typeName} Bar -//{{ -//}}"; -// return VerifyNoItemForDocumentAsync(markup, LanguageNames.CSharp); -// } - -// [Theory] -// [InlineData("public Bar() { }")] -// [InlineData("public static void Bar3() { }")] -// [InlineData("public static void ~Bar() { }")] -// [InlineData("public static Bar operator +(Bar a, Bar b) => new Bar();")] -// public Task TestCSharpSpecialMember(string memberDeclaration) -// { -// var markup = $@" -//public abstract class {{|target1:Bar1|}} -//{{}} -//public class Bar : Bar1 -//{{ -// {{|{SearchAreaTag}:{memberDeclaration}|}} -//}}"; -// return VerifyInSingleDocumentAsync( -// markup, -// LanguageNames.CSharp, -// new TestInheritanceMemberItem( -// lineNumber: 4, -// memberName: "class Bar", -// targets: ImmutableArray.Create( -// new TargetInfo( -// targetSymbolDisplayName: "class Bar1", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implementing)))); -// } - -// [Fact] -// public Task TestCSharpMetadataInterface() -// { -// var markup = @" -//using System.Collections; -//public class Bar : IEnumerable -//{ -//}"; -// return VerifyInSingleDocumentAsync( -// markup, -// LanguageNames.CSharp, -// new TestInheritanceMemberItem( -// lineNumber: 3, -// memberName: "class Bar", -// targets: ImmutableArray.Create( -// new TargetInfo( -// targetSymbolDisplayName: "interface IEnumerable", -// relationship: InheritanceRelationship.Implementing, -// inMetadata: true)))); -// } - -// [Fact] -// public Task TestCSharpEventDeclaration() -// { -// var markup = @" -//using System; -//interface {|target2:IBar|} -//{ -// event EventHandler {|target4:e|}; -//} -//public class {|target1:Bar|} : IBar -//{ -// public event EventHandler {|target3:e|} -// { -// add {} remove {} -// } -//}"; -// var itemForIBar = new TestInheritanceMemberItem( -// lineNumber: 3, -// memberName: "interface IBar", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "class Bar", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForBar = new TestInheritanceMemberItem( -// lineNumber: 7, -// memberName: "class Bar", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "interface IBar", -// locationTag: "target2", -// relationship: InheritanceRelationship.Implementing))); - -// var itemForEventInInterface = new TestInheritanceMemberItem( -// lineNumber: 5, -// memberName: "event EventHandler IBar.e", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "event EventHandler Bar.e", -// locationTag: "target3", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForEventInClass = new TestInheritanceMemberItem( -// lineNumber: 9, -// memberName: "event EventHandler Bar.e", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "event EventHandler IBar.e", -// locationTag: "target4", -// relationship: InheritanceRelationship.Implementing))); - -// return VerifyInSingleDocumentAsync( -// markup, -// LanguageNames.CSharp, -// itemForIBar, -// itemForBar, -// itemForEventInInterface, -// itemForEventInClass); -// } - -// [Fact] -// public Task TestCSharpEventFieldDeclarations() -// { -// var markup = @"using System; -//interface {|target2:IBar|} -//{ -// event EventHandler {|target5:e1|}, {|target6:e2|}; -//} -//public class {|target1:Bar|} : IBar -//{ -// public event EventHandler {|target3:e1|}, {|target4:e2|}; -//}"; -// var itemForIBar = new TestInheritanceMemberItem( -// lineNumber: 2, -// memberName: "interface IBar", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "class Bar", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForBar = new TestInheritanceMemberItem( -// lineNumber: 6, -// memberName: "class Bar", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "interface IBar", -// locationTag: "target2", -// relationship: InheritanceRelationship.Implementing))); - -// var itemForE1InInterface = new TestInheritanceMemberItem( -// lineNumber: 4, -// memberName: "event EventHandler IBar.e1", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "event EventHandler Bar.e1", -// locationTag: "target3", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForE2InInterface = new TestInheritanceMemberItem( -// lineNumber: 4, -// memberName: "event EventHandler IBar.e2", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "event EventHandler Bar.e2", -// locationTag: "target4", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForE1InClass = new TestInheritanceMemberItem( -// lineNumber: 8, -// memberName: "event EventHandler Bar.e1", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "event EventHandler IBar.e1", -// locationTag: "target5", -// relationship: InheritanceRelationship.Implementing))); - -// var itemForE2InClass = new TestInheritanceMemberItem( -// lineNumber: 8, -// memberName: "event EventHandler Bar.e2", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "event EventHandler IBar.e2", -// locationTag: "target6", -// relationship: InheritanceRelationship.Implementing))); - -// return VerifyInSingleDocumentAsync( -// markup, -// LanguageNames.CSharp, -// itemForIBar, -// itemForBar, -// itemForE1InInterface, -// itemForE2InInterface, -// itemForE1InClass, -// itemForE2InClass); -// } - -// [Fact] -// public Task TestCSharpInterfaceMembers() -// { -// var markup = @"using System; -//interface {|target1:IBar|} -//{ -// void {|target4:Foo|}(); -// int {|target6:Poo|} { get; set; } -// event EventHandler {|target8:Eoo|}; -// int {|target9:this|}[int i] { get; set; } -//} -//public class {|target2:Bar|} : IBar -//{ -// public void {|target3:Foo|}() { } -// public int {|target5:Poo|} { get; set; } -// public event EventHandler {|target7:Eoo|}; -// public int {|target10:this|}[int i] { get => 1; set { } } -//}"; -// var itemForEooInClass = new TestInheritanceMemberItem( -// lineNumber: 13, -// memberName: "event EventHandler Bar.Eoo", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "event EventHandler IBar.Eoo", -// locationTag: "target8", -// relationship: InheritanceRelationship.Implementing)) -// ); - -// var itemForEooInInterface = new TestInheritanceMemberItem( -// lineNumber: 6, -// memberName: "event EventHandler IBar.Eoo", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "event EventHandler Bar.Eoo", -// locationTag: "target7", -// relationship: InheritanceRelationship.Implemented)) -// ); - -// var itemForPooInInterface = new TestInheritanceMemberItem( -// lineNumber: 5, -// memberName: "int IBar.Poo { get; set; }", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "int Bar.Poo { get; set; }", -// locationTag: "target5", -// relationship: InheritanceRelationship.Implemented)) -// ); - -// var itemForPooInClass = new TestInheritanceMemberItem( -// lineNumber: 12, -// memberName: "int Bar.Poo { get; set; }", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "int IBar.Poo { get; set; }", -// locationTag: "target6", -// relationship: InheritanceRelationship.Implementing)) -// ); - -// var itemForFooInInterface = new TestInheritanceMemberItem( -// lineNumber: 4, -// memberName: "void IBar.Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "void Bar.Foo()", -// locationTag: "target3", -// relationship: InheritanceRelationship.Implemented)) -// ); - -// var itemForFooInClass = new TestInheritanceMemberItem( -// lineNumber: 11, -// memberName: "void Bar.Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "void IBar.Foo()", -// locationTag: "target4", -// relationship: InheritanceRelationship.Implementing)) -// ); - -// var itemForIBar = new TestInheritanceMemberItem( -// lineNumber: 2, -// memberName: "interface IBar", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "class Bar", -// locationTag: "target2", -// relationship: InheritanceRelationship.Implemented)) -// ); - -// var itemForBar = new TestInheritanceMemberItem( -// lineNumber: 9, -// memberName: "class Bar", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "interface IBar", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implementing)) -// ); - -// var itemForIndexerInClass = new TestInheritanceMemberItem( -// lineNumber: 14, -// memberName: "int Bar.this[int] { get; set; }", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "int IBar.this[int] { get; set; }", -// locationTag: "target9", -// relationship: InheritanceRelationship.Implementing)) -// ); - -// var itemForIndexerInInterface = new TestInheritanceMemberItem( -// lineNumber: 7, -// memberName: "int IBar.this[int] { get; set; }", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "int Bar.this[int] { get; set; }", -// locationTag: "target10", -// relationship: InheritanceRelationship.Implemented)) -// ); - -// return VerifyInSingleDocumentAsync( -// markup, -// LanguageNames.CSharp, -// itemForEooInClass, -// itemForEooInInterface, -// itemForPooInInterface, -// itemForPooInClass, -// itemForFooInInterface, -// itemForFooInClass, -// itemForIBar, -// itemForBar, -// itemForIndexerInInterface, -// itemForIndexerInClass); -// } - -// [Theory] -// [InlineData("abstract")] -// [InlineData("virtual")] -// public Task TestCSharpAbstractClassMembers(string modifier) -// { -// var markup = $@"using System; -//public abstract class {{|target2:Bar|}} -//{{ -// public {modifier} void {{|target4:Foo|}}(); -// public {modifier} int {{|target6:Poo|}} {{ get; set; }} -// public {modifier} event EventHandler {{|target8:Eoo|}}; -//}} -//public class {{|target1:Bar2|}} : Bar -//{{ -// public override void {{|target3:Foo|}}() {{ }} -// public override int {{|target5:Poo|}} {{ get; set; }} -// public override event EventHandler {{|target7:Eoo|}}; -//}} -// "; - -// var itemForEooInClass = new TestInheritanceMemberItem( -// lineNumber: 12, -// memberName: "override event EventHandler Bar2.Eoo", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: $"{modifier} event EventHandler Bar.Eoo", -// locationTag: "target8", -// relationship: InheritanceRelationship.Overriding))); - -// var itemForEooInAbstractClass = new TestInheritanceMemberItem( -// lineNumber: 6, -// memberName: $"{modifier} event EventHandler Bar.Eoo", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "override event EventHandler Bar2.Eoo", -// locationTag: "target7", -// relationship: InheritanceRelationship.Overridden))); - -// var itemForPooInClass = new TestInheritanceMemberItem( -// lineNumber: 11, -// memberName: "override int Bar2.Poo { get; set; }", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: $"{modifier} int Bar.Poo {{ get; set; }}", -// locationTag: "target6", -// relationship: InheritanceRelationship.Overriding))); - -// var itemForPooInAbstractClass = new TestInheritanceMemberItem( -// lineNumber: 5, -// memberName: $"{modifier} int Bar.Poo {{ get; set; }}", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "override int Bar2.Poo { get; set; }", -// locationTag: "target5", -// relationship: InheritanceRelationship.Overridden))); - -// var itemForFooInAbstractClass = new TestInheritanceMemberItem( -// lineNumber: 4, -// memberName: $"{modifier} void Bar.Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "override void Bar2.Foo()", -// locationTag: "target3", -// relationship: InheritanceRelationship.Overridden))); - -// var itemForFooInClass = new TestInheritanceMemberItem( -// lineNumber: 10, -// memberName: "override void Bar2.Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: $"{modifier} void Bar.Foo()", -// locationTag: "target4", -// relationship: InheritanceRelationship.Overriding))); - -// var itemForBar = new TestInheritanceMemberItem( -// lineNumber: 2, -// memberName: "class Bar", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "class Bar2", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForBar2 = new TestInheritanceMemberItem( -// lineNumber: 8, -// memberName: "class Bar2", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "class Bar", -// locationTag: "target2", -// relationship: InheritanceRelationship.Implementing))); - -// return VerifyInSingleDocumentAsync( -// markup, -// LanguageNames.CSharp, -// itemForBar, -// itemForBar2, -// itemForFooInAbstractClass, -// itemForFooInClass, -// itemForPooInClass, -// itemForPooInAbstractClass, -// itemForEooInClass, -// itemForEooInAbstractClass); -// } - -// [Theory] -// [CombinatorialData] -// public Task TestCSharpOverrideMemberCanFindImplementingInterface(bool testDuplicate) -// { -// var markup1 = @"using System; -//public interface {|target4:IBar|} -//{ -// void {|target6:Foo|}(); -//} -//public class {|target1:Bar1|} : IBar -//{ -// public virtual void {|target2:Foo|}() { } -//} -//public class {|target5:Bar2|} : Bar1 -//{ -// public override void {|target3:Foo|}() { } -//}"; - -// var markup2 = @"using System; -//public interface {|target4:IBar|} -//{ -// void {|target6:Foo|}(); -//} -//public class {|target1:Bar1|} : IBar -//{ -// public virtual void {|target2:Foo|}() { } -//} -//public class {|target5:Bar2|} : Bar1, IBar -//{ -// public override void {|target3:Foo|}() { } -//}"; - -// var itemForIBar = new TestInheritanceMemberItem( -// lineNumber: 2, -// memberName: "interface IBar", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "class Bar1", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implemented), -// new TargetInfo( -// targetSymbolDisplayName: "class Bar2", -// locationTag: "target5", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForFooInIBar = new TestInheritanceMemberItem( -// lineNumber: 4, -// memberName: "void IBar.Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "virtual void Bar1.Foo()", -// locationTag: "target2", -// relationship: InheritanceRelationship.Implemented), -// new TargetInfo( -// targetSymbolDisplayName: "override void Bar2.Foo()", -// locationTag: "target3", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForBar1 = new TestInheritanceMemberItem( -// lineNumber: 6, -// memberName: "class Bar1", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "interface IBar", -// locationTag: "target4", -// relationship: InheritanceRelationship.Implementing), -// new TargetInfo( -// targetSymbolDisplayName: "class Bar2", -// locationTag: "target5", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForFooInBar1 = new TestInheritanceMemberItem( -// lineNumber: 8, -// memberName: "virtual void Bar1.Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "void IBar.Foo()", -// locationTag: "target6", -// relationship: InheritanceRelationship.Implementing), -// new TargetInfo( -// targetSymbolDisplayName: "override void Bar2.Foo()", -// locationTag: "target3", -// relationship: InheritanceRelationship.Overridden))); - -// var itemForBar2 = new TestInheritanceMemberItem( -// lineNumber: 10, -// memberName: "class Bar2", -// targets: ImmutableArray.Create( -// new TargetInfo( -// targetSymbolDisplayName: "class Bar1", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implementing), -// new TargetInfo( -// targetSymbolDisplayName: "interface IBar", -// locationTag: "target4", -// relationship: InheritanceRelationship.Implementing))); - -// var itemForFooInBar2 = new TestInheritanceMemberItem( -// lineNumber: 12, -// memberName: "override void Bar2.Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "void IBar.Foo()", -// locationTag: "target6", -// relationship: InheritanceRelationship.Implementing), -// new TargetInfo( -// targetSymbolDisplayName: "virtual void Bar1.Foo()", -// locationTag: "target2", -// relationship: InheritanceRelationship.Overriding))); - -// return VerifyInSingleDocumentAsync( -// testDuplicate ? markup2 : markup1, -// LanguageNames.CSharp, -// itemForIBar, -// itemForFooInIBar, -// itemForBar1, -// itemForFooInBar1, -// itemForBar2, -// itemForFooInBar2); -// } - -// [Fact] -// public Task TestCSharpFindGenericsBaseType() -// { -// var lessThanToken = SecurityElement.Escape("<"); -// var greaterThanToken = SecurityElement.Escape(">"); -// var markup = $@" -//public interface {{|target2:IBar|}}{lessThanToken}T{greaterThanToken} -//{{ -// void {{|target4:Foo|}}(); -//}} - -//public class {{|target1:Bar2|}} : IBar{lessThanToken}int{greaterThanToken}, IBar{lessThanToken}string{greaterThanToken} -//{{ -// public void {{|target3:Foo|}}(); -//}}"; - -// var itemForIBar = new TestInheritanceMemberItem( -// lineNumber: 2, -// memberName: "interface IBar", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "class Bar2", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForFooInIBar = new TestInheritanceMemberItem( -// lineNumber: 4, -// memberName: "void IBar.Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "void Bar2.Foo()", -// locationTag: "target3", -// relationship: InheritanceRelationship.Implemented))); - -// // Only have one IBar item -// var itemForBar2 = new TestInheritanceMemberItem( -// lineNumber: 7, -// memberName: "class Bar2", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "interface IBar", -// locationTag: "target2", -// relationship: InheritanceRelationship.Implementing))); - -// // Only have one IBar.Foo item -// var itemForFooInBar2 = new TestInheritanceMemberItem( -// lineNumber: 9, -// memberName: "void Bar2.Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "void IBar.Foo()", -// locationTag: "target4", -// relationship: InheritanceRelationship.Implementing))); - -// return VerifyInSingleDocumentAsync( -// markup, -// LanguageNames.CSharp, -// itemForIBar, -// itemForFooInIBar, -// itemForBar2, -// itemForFooInBar2); -// } - -// #endregion - -// #region TestsForVisualBasic - -// [Fact] -// public Task TestVisualBasicWithErrorBaseType() -// { -// var markup = @" -//Namespace MyNamespace -// Public Class Bar -// Implements SomethingNotExist -// End Class -//End Namespace"; - -// return VerifyNoItemForDocumentAsync(markup, LanguageNames.VisualBasic); -// } - -// [Fact] -// public Task TestVisualBasicReferencingMetadata() -// { -// var markup = @" -//Namespace MyNamespace -// Public Class Bar -// Implements System.Collections.IEnumerable -// Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator -// Throw New NotImplementedException() -// End Function -// End Class -//End Namespace"; -// var itemForBar = new TestInheritanceMemberItem( -// lineNumber: 3, -// memberName: "Class Bar", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Interface IEnumerable", -// relationship: InheritanceRelationship.Implementing, -// inMetadata: true))); - -// var itemForGetEnumerator = new TestInheritanceMemberItem( -// lineNumber: 5, -// memberName: "Function Bar.GetEnumerator() As IEnumerator", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Function IEnumerable.GetEnumerator() As IEnumerator", -// relationship: InheritanceRelationship.Implementing, -// inMetadata: true))); - -// return VerifyInSingleDocumentAsync(markup, LanguageNames.VisualBasic, itemForBar, itemForGetEnumerator); -// } - -// [Fact] -// public Task TestVisualBasicClassImplementingInterface() -// { -// var markup = @" -//Interface {|target2:IBar|} -//End Interface -//Class {|target1:Bar|} -// Implements IBar -//End Class"; -// var itemForIBar = new TestInheritanceMemberItem( -// lineNumber: 2, -// memberName: "Interface IBar", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Class Bar", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForBar = new TestInheritanceMemberItem( -// lineNumber: 4, -// memberName: "Class Bar", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Interface IBar", -// locationTag: "target2", -// relationship: InheritanceRelationship.Implementing))); - -// return VerifyInSingleDocumentAsync( -// markup, -// LanguageNames.VisualBasic, -// itemForIBar, -// itemForBar); -// } - -// [Fact] -// public Task TestVisualBasicInterfaceImplementingInterface() -// { -// var markup = @" -//Interface {|target2:IBar2|} -//End Interface -//Interface {|target1:IBar|} -// Inherits IBar2 -//End Interface"; - -// var itemForIBar2 = new TestInheritanceMemberItem( -// lineNumber: 2, -// memberName: "Interface IBar2", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Interface IBar", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForIBar = new TestInheritanceMemberItem( -// lineNumber: 4, -// memberName: "Interface IBar", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Interface IBar2", -// locationTag: "target2", -// relationship: InheritanceRelationship.Implementing))); -// return VerifyInSingleDocumentAsync(markup, LanguageNames.VisualBasic, itemForIBar2, itemForIBar); -// } - -// [Fact] -// public Task TestVisualBasicClassInheritsClass() -// { -// var markup = @" -//Class {|target2:Bar2|} -//End Class -//Class {|target1:Bar|} -// Inherits Bar2 -//End Class"; - -// var itemForBar2 = new TestInheritanceMemberItem( -// lineNumber: 2, -// memberName: "Class Bar2", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Class Bar", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForBar = new TestInheritanceMemberItem( -// lineNumber: 4, -// memberName: "Class Bar", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Class Bar2", -// locationTag: "target2", -// relationship: InheritanceRelationship.Implementing))); -// return VerifyInSingleDocumentAsync(markup, LanguageNames.VisualBasic, itemForBar2, itemForBar); -// } - -// [Theory] -// [InlineData("Class")] -// [InlineData("Structure")] -// [InlineData("Enum")] -// [InlineData("Interface")] -// public Task TestVisualBasicTypeWithoutBaseType(string typeName) -// { -// var markup = $@" -//{typeName} Bar -//End {typeName}"; - -// return VerifyNoItemForDocumentAsync(markup, LanguageNames.VisualBasic); -// } - -// [Fact] -// public Task TestVisualBasicMetadataInterface() -// { -// var markup = @" -//Imports System.Collections -//Class Bar -// Implements IEnumerable -//End Class"; -// return VerifyInSingleDocumentAsync( -// markup, -// LanguageNames.VisualBasic, -// new TestInheritanceMemberItem( -// lineNumber: 3, -// memberName: "Class Bar", -// targets: ImmutableArray.Create( -// new TargetInfo( -// targetSymbolDisplayName: "Interface IEnumerable", -// relationship: InheritanceRelationship.Implementing, -// inMetadata: true)))); -// } - -// [Fact] -// public Task TestVisualBasicEventStatement() -// { -// var markup = @" -//Interface {|target2:IBar|} -// Event {|target4:e|} As EventHandler -//End Interface -//Class {|target1:Bar|} -// Implements IBar -// Public Event {|target3:e|} As EventHandler Implements IBar.e -//End Class"; - -// var itemForIBar = new TestInheritanceMemberItem( -// lineNumber: 2, -// memberName: "Interface IBar", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Class Bar", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForBar = new TestInheritanceMemberItem( -// lineNumber: 5, -// memberName: "Class Bar", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Interface IBar", -// locationTag: "target2", -// relationship: InheritanceRelationship.Implementing))); - -// var itemForEventInInterface = new TestInheritanceMemberItem( -// lineNumber: 3, -// memberName: "Event IBar.e As EventHandler", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Event Bar.e As EventHandler", -// locationTag: "target3", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForEventInClass = new TestInheritanceMemberItem( -// lineNumber: 7, -// memberName: "Event Bar.e As EventHandler", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Event IBar.e As EventHandler", -// locationTag: "target4", -// relationship: InheritanceRelationship.Implementing))); - -// return VerifyInSingleDocumentAsync( -// markup, -// LanguageNames.VisualBasic, -// itemForIBar, -// itemForBar, -// itemForEventInInterface, -// itemForEventInClass); -// } - -// [Fact] -// public Task TestVisualBasicEventBlock() -// { -// var markup = @" -//Interface {|target2:IBar|} -// Event {|target4:e|} As EventHandler -//End Interface -//Class {|target1:Bar|} -// Implements IBar -// Public Custom Event {|target3:e|} As EventHandler Implements IBar.e -// End Event -//End Class"; -// var itemForIBar = new TestInheritanceMemberItem( -// lineNumber: 2, -// memberName: "Interface IBar", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Class Bar", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForBar = new TestInheritanceMemberItem( -// lineNumber: 5, -// memberName: "Class Bar", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Interface IBar", -// locationTag: "target2", -// relationship: InheritanceRelationship.Implementing))); - -// var itemForEventInInterface = new TestInheritanceMemberItem( -// lineNumber: 3, -// memberName: "Event IBar.e As EventHandler", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Event Bar.e As EventHandler", -// locationTag: "target3", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForEventInClass = new TestInheritanceMemberItem( -// lineNumber: 7, -// memberName: "Event Bar.e As EventHandler", -// ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Event IBar.e As EventHandler", -// locationTag: "target4", -// relationship: InheritanceRelationship.Implementing))); - -// return VerifyInSingleDocumentAsync( -// markup, -// LanguageNames.VisualBasic, -// itemForIBar, -// itemForBar, -// itemForEventInInterface, -// itemForEventInClass); -// } - -// [Fact] -// public Task TestVisualBasicInterfaceMembers() -// { -// var markup = @" -//Interface {|target2:IBar|} -// Property {|target4:Poo|} As Integer -// Function {|target6:Foo|}() As Integer -//End Interface - -//Class {|target1:Bar|} -// Implements IBar -// Public Property {|target3:Poo|} As Integer Implements IBar.Poo -// Get -// Return 1 -// End Get -// Set(value As Integer) -// End Set -// End Property -// Public Function {|target5:Foo|}() As Integer Implements IBar.Foo -// Return 1 -// End Function -//End Class"; -// var itemForIBar = new TestInheritanceMemberItem( -// lineNumber: 2, -// memberName: "Interface IBar", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Class Bar", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForBar = new TestInheritanceMemberItem( -// lineNumber: 7, -// memberName: "Class Bar", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Interface IBar", -// locationTag: "target2", -// relationship: InheritanceRelationship.Implementing))); - -// var itemForPooInInterface = new TestInheritanceMemberItem( -// lineNumber: 3, -// memberName: "Property IBar.Poo As Integer", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Property Bar.Poo As Integer", -// locationTag: "target3", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForPooInClass = new TestInheritanceMemberItem( -// lineNumber: 9, -// memberName: "Property Bar.Poo As Integer", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Property IBar.Poo As Integer", -// locationTag: "target4", -// relationship: InheritanceRelationship.Implementing))); - -// var itemForFooInInterface = new TestInheritanceMemberItem( -// lineNumber: 4, -// memberName: "Function IBar.Foo() As Integer", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Function Bar.Foo() As Integer", -// locationTag: "target5", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForFooInClass = new TestInheritanceMemberItem( -// lineNumber: 16, -// memberName: "Function Bar.Foo() As Integer", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Function IBar.Foo() As Integer", -// locationTag: "target6", -// relationship: InheritanceRelationship.Implementing))); - -// return VerifyInSingleDocumentAsync( -// markup, -// LanguageNames.VisualBasic, -// itemForIBar, -// itemForBar, -// itemForPooInInterface, -// itemForPooInClass, -// itemForFooInInterface, -// itemForFooInClass); -// } - -// [Fact] -// public Task TestVisualBasicMustInheritClassMember() -// { -// var markup = @" -//MustInherit Class {|target2:Bar1|} -// Public MustOverride Sub {|target4:Foo|}() -//End Class - -//Class {|target1:Bar|} -// Inherits Bar1 -// Public Overrides Sub {|target3:Foo|}() -// End Sub -//End Class"; -// var itemForBar1 = new TestInheritanceMemberItem( -// lineNumber: 2, -// memberName: "Class Bar1", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: $"Class Bar", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForBar = new TestInheritanceMemberItem( -// lineNumber: 6, -// memberName: "Class Bar", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Class Bar1", -// locationTag: "target2", -// relationship: InheritanceRelationship.Implementing))); - -// var itemForFooInBar1 = new TestInheritanceMemberItem( -// lineNumber: 3, -// memberName: "MustOverride Sub Bar1.Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Overrides Sub Bar.Foo()", -// locationTag: "target3", -// relationship: InheritanceRelationship.Overridden))); - -// var itemForFooInBar = new TestInheritanceMemberItem( -// lineNumber: 8, -// memberName: "Overrides Sub Bar.Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "MustOverride Sub Bar1.Foo()", -// locationTag: "target4", -// relationship: InheritanceRelationship.Overriding))); - -// return VerifyInSingleDocumentAsync( -// markup, -// LanguageNames.VisualBasic, -// itemForBar1, -// itemForBar, -// itemForFooInBar1, -// itemForFooInBar); -// } - -// [Theory] -// [CombinatorialData] -// public Task TestVisualBasicOverrideMemberCanFindImplementingInterface(bool testDuplicate) -// { -// var markup1 = @" -//Interface {|target4:IBar|} -// Sub {|target6:Foo|}() -//End Interface - -//Class {|target1:Bar1|} -// Implements IBar -// Public Overridable Sub {|target2:Foo|}() Implements IBar.Foo -// End Sub -//End Class - -//Class {|target5:Bar2|} -// Inherits Bar1 -// Public Overrides Sub {|target3:Foo|}() -// End Sub -//End Class"; - -// var markup2 = @" -//Interface {|target4:IBar|} -// Sub {|target6:Foo|}() -//End Interface - -//Class {|target1:Bar1|} -// Implements IBar -// Public Overridable Sub {|target2:Foo|}() Implements IBar.Foo -// End Sub -//End Class - -//Class {|target5:Bar2|} -// Inherits Bar1 -// Public Overrides Sub {|target3:Foo|}() -// End Sub -//End Class"; -// var itemForIBar = new TestInheritanceMemberItem( -// lineNumber: 2, -// memberName: "Interface IBar", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Class Bar1", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implemented), -// new TargetInfo( -// targetSymbolDisplayName: "Class Bar2", -// locationTag: "target5", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForFooInIBar = new TestInheritanceMemberItem( -// lineNumber: 3, -// memberName: "Sub IBar.Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Overridable Sub Bar1.Foo()", -// locationTag: "target2", -// relationship: InheritanceRelationship.Implemented), -// new TargetInfo( -// targetSymbolDisplayName: "Overrides Sub Bar2.Foo()", -// locationTag: "target3", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForBar1 = new TestInheritanceMemberItem( -// lineNumber: 6, -// memberName: "Class Bar1", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Interface IBar", -// locationTag: "target4", -// relationship: InheritanceRelationship.Implementing), -// new TargetInfo( -// targetSymbolDisplayName: "Class Bar2", -// locationTag: "target5", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForFooInBar1 = new TestInheritanceMemberItem( -// lineNumber: 8, -// memberName: "Overridable Sub Bar1.Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Sub IBar.Foo()", -// locationTag: "target6", -// relationship: InheritanceRelationship.Implementing), -// new TargetInfo( -// targetSymbolDisplayName: "Overrides Sub Bar2.Foo()", -// locationTag: "target3", -// relationship: InheritanceRelationship.Overridden))); - -// var itemForBar2 = new TestInheritanceMemberItem( -// lineNumber: 12, -// memberName: "Class Bar2", -// targets: ImmutableArray.Create( -// new TargetInfo( -// targetSymbolDisplayName: "Class Bar1", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implementing), -// new TargetInfo( -// targetSymbolDisplayName: "Interface IBar", -// locationTag: "target4", -// relationship: InheritanceRelationship.Implementing))); - -// var itemForFooInBar2 = new TestInheritanceMemberItem( -// lineNumber: 14, -// memberName: "Overrides Sub Bar2.Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Sub IBar.Foo()", -// locationTag: "target6", -// relationship: InheritanceRelationship.Implementing), -// new TargetInfo( -// targetSymbolDisplayName: "Overridable Sub Bar1.Foo()", -// locationTag: "target2", -// relationship: InheritanceRelationship.Overriding))); - -// return VerifyInSingleDocumentAsync( -// testDuplicate ? markup2 : markup1, -// LanguageNames.VisualBasic, -// itemForIBar, -// itemForFooInIBar, -// itemForBar1, -// itemForFooInBar1, -// itemForBar2, -// itemForFooInBar2); -// } - -// [Fact] -// public Task TestVisualBasicFindGenericsBaseType() -// { -// var markup = @" -//Public Interface {|target5:IBar|}(Of T) -// Sub {|target6:Foo|}() -//End Interface - -//Public Class {|target1:Bar|} -// Implements IBar(Of Integer) -// Implements IBar(Of String) - -// Public Sub {|target3:Foo|}() Implements IBar(Of Integer).Foo -// Throw New NotImplementedException() -// End Sub - -// Private Sub {|target4:IBar_Foo|}() Implements IBar(Of String).Foo -// Throw New NotImplementedException() -// End Sub -//End Class"; - -// var itemForIBar = new TestInheritanceMemberItem( -// lineNumber: 2, -// memberName: "Interface IBar(Of T)", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Class Bar", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForFooInIBar = new TestInheritanceMemberItem( -// lineNumber: 3, -// memberName: "Sub IBar(Of T).Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Sub Bar.Foo()", -// locationTag: "target3", -// relationship: InheritanceRelationship.Implemented), -// new TargetInfo( -// targetSymbolDisplayName: "Sub Bar.IBar_Foo()", -// locationTag: "target4", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForBar = new TestInheritanceMemberItem( -// lineNumber: 6, -// memberName: "Class Bar", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Interface IBar(Of T)", -// locationTag: "target5", -// relationship: InheritanceRelationship.Implementing))); - -// var itemForFooInBar = new TestInheritanceMemberItem( -// lineNumber: 10, -// memberName: "Sub Bar.Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Sub IBar(Of T).Foo()", -// locationTag: "target6", -// relationship: InheritanceRelationship.Implementing))); - -// var itemForIBar_FooInBar = new TestInheritanceMemberItem( -// lineNumber: 14, -// memberName: "Sub Bar.IBar_Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Sub IBar(Of T).Foo()", -// locationTag: "target6", -// relationship: InheritanceRelationship.Implementing))); - -// return VerifyInSingleDocumentAsync( -// markup, -// LanguageNames.VisualBasic, -// itemForIBar, -// itemForFooInIBar, -// itemForBar, -// itemForFooInBar, -// itemForIBar_FooInBar); -// } - -// #endregion - -// [Fact] -// public Task TestCSharpProjectReferencingVisualBasicProject() -// { -// var markup1 = @" -//using MyNamespace; -//namespace BarNs -//{ -// public class {|target2:Bar|} : IBar -// { -// public void {|target4:Foo|}() { } -// } -//}"; - -// var markup2 = @" -//Namespace MyNamespace -// Public Interface {|target1:IBar|} -// Sub {|target3:Foo|}() -// End Interface -//End Namespace"; - -// var itemForBar = new TestInheritanceMemberItem( -// lineNumber: 5, -// memberName: "class Bar", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Interface IBar", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implementing))); - -// var itemForFooInMarkup1 = new TestInheritanceMemberItem( -// lineNumber: 7, -// memberName: "void Bar.Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Sub IBar.Foo()", -// locationTag: "target3", -// relationship: InheritanceRelationship.Implementing))); - -// var itemForIBar = new TestInheritanceMemberItem( -// lineNumber: 3, -// memberName: "Interface IBar", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "class Bar", -// locationTag: "target2", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForFooInMarkup2 = new TestInheritanceMemberItem( -// lineNumber: 4, -// memberName: "Sub IBar.Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "void Bar.Foo()", -// locationTag: "target4", -// relationship: InheritanceRelationship.Implemented))); - -// return VerifyInDifferentProjectsAsync( -// (markup1, LanguageNames.CSharp), -// (markup2, LanguageNames.VisualBasic), -// new[] { itemForBar, itemForFooInMarkup1 }, -// new[] { itemForIBar, itemForFooInMarkup2 }); -// } - -// [Fact] -// public Task TestVisualBasicProjectReferencingCSharpProject() -// { -// var markup1 = @" -//Imports BarNs -//Namespace MyNamespace -// Public Class {|target2:Bar44|} -// Implements IBar - -// Public Sub {|target4:Foo|}() Implements IBar.Foo -// End Sub -// End Class -//End Namespace"; - -// var markup2 = @" -//namespace BarNs -//{ -// public interface {|target1:IBar|} -// { -// void {|target3:Foo|}(); -// } -//}"; - -// var itemForBar44 = new TestInheritanceMemberItem( -// lineNumber: 4, -// memberName: "Class Bar44", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "interface IBar", -// locationTag: "target1", -// relationship: InheritanceRelationship.Implementing))); - -// var itemForFooInMarkup1 = new TestInheritanceMemberItem( -// lineNumber: 7, -// memberName: "Sub Bar44.Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "void IBar.Foo()", -// locationTag: "target3", -// relationship: InheritanceRelationship.Implementing))); - -// var itemForIBar = new TestInheritanceMemberItem( -// lineNumber: 4, -// memberName: "interface IBar", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Class Bar44", -// locationTag: "target2", -// relationship: InheritanceRelationship.Implemented))); - -// var itemForFooInMarkup2 = new TestInheritanceMemberItem( -// lineNumber: 6, -// memberName: "void IBar.Foo()", -// targets: ImmutableArray.Create(new TargetInfo( -// targetSymbolDisplayName: "Sub Bar44.Foo()", -// locationTag: "target4", -// relationship: InheritanceRelationship.Implemented))); - -// return VerifyInDifferentProjectsAsync( -// (markup1, LanguageNames.VisualBasic), -// (markup2, LanguageNames.CSharp), -// new[] { itemForBar44, itemForFooInMarkup1 }, -// new[] { itemForIBar, itemForFooInMarkup2 }); -// } -// } -//} +using System.Collections.Immutable; +using System.Linq; +using System.Security; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.InheritanceMargin; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.UnitTests.InheritanceMargin +{ + [Trait(Traits.Feature, Traits.Features.InheritanceMargin)] + [UseExportProvider] + public class InheritanceMarginTests + { + private const string SearchAreaTag = "SeachTag"; + + #region Helpers + + private static Task VerifyNoItemForDocumentAsync(string markup, string languageName) + => VerifyInSingleDocumentAsync(markup, languageName); + + private static Task VerifyInSingleDocumentAsync( + string markup, + string languageName, + params TestInheritanceMemberItem[] memberItems) + { + var workspaceFile = $@" + + + + {markup} + + +"; + + var cancellationToken = CancellationToken.None; + + using var testWorkspace = TestWorkspace.Create( + workspaceFile, + composition: EditorTestCompositions.EditorFeatures); + + var testHostDocument = testWorkspace.Documents[0]; + return VerifyTestMemberInDocumentAsync(testWorkspace, testHostDocument, memberItems, cancellationToken); + } + + private static async Task VerifyTestMemberInDocumentAsync( + TestWorkspace testWorkspace, + TestHostDocument testHostDocument, + TestInheritanceMemberItem[] memberItems, + CancellationToken cancellationToken) + { + var document = testWorkspace.CurrentSolution.GetRequiredDocument(testHostDocument.Id); + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var searchingSpan = root.Span; + // Look for the search span, if not found, then pass the whole document span to the service. + if (testHostDocument.AnnotatedSpans.TryGetValue(SearchAreaTag, out var spans) && spans.IsSingle()) + { + searchingSpan = spans[0]; + } + + var service = document.GetRequiredLanguageService(); + var actualItems = await service.GetInheritanceMemberItemsAsync( + document, + searchingSpan, + cancellationToken).ConfigureAwait(false); + + var sortedActualItems = actualItems.OrderBy(item => item.LineNumber).ToImmutableArray(); + var sortedExpectedItems = memberItems.OrderBy(item => item.LineNumber).ToImmutableArray(); + Assert.Equal(sortedExpectedItems.Length, sortedActualItems.Length); + + for (var i = 0; i < sortedActualItems.Length; i++) + { + VerifyInheritanceMember(testWorkspace, sortedExpectedItems[i], sortedActualItems[i]); + } + } + + private static void VerifyInheritanceMember(TestWorkspace testWorkspace, TestInheritanceMemberItem expectedItem, InheritanceMarginItem actualItem) + { + Assert.Equal(expectedItem.LineNumber, actualItem.LineNumber); + Assert.Equal(expectedItem.MemberName, actualItem.DisplayTexts.JoinText()); + Assert.Equal(expectedItem.Targets.Length, actualItem.TargetItems.Length); + var expectedTargets = expectedItem.Targets + .Select(info => TestInheritanceTargetItem.Create(info, testWorkspace)) + .OrderBy(target => target.TargetSymbolName) + .ToImmutableArray(); + var sortedActualTargets = actualItem.TargetItems.OrderBy(target => target.DefinitionItem.DisplayParts.JoinText()) + .ToImmutableArray(); + for (var i = 0; i < expectedTargets.Length; i++) + { + VerifyInheritanceTarget(expectedTargets[i], sortedActualTargets[i]); + } + } + + private static void VerifyInheritanceTarget(TestInheritanceTargetItem expectedTarget, InheritanceTargetItem actualTarget) + { + Assert.Equal(expectedTarget.TargetSymbolName, actualTarget.DefinitionItem.DisplayParts.JoinText()); + Assert.Equal(expectedTarget.RelationshipToMember, actualTarget.RelationToMember); + + if (expectedTarget.IsInMetadata) + { + Assert.True(actualTarget.DefinitionItem.Properties.ContainsKey("MetadataSymbolKey")); + Assert.True(actualTarget.DefinitionItem.SourceSpans.IsEmpty); + } + else + { + var actualDocumentSpans = actualTarget.DefinitionItem.SourceSpans.OrderBy(documentSpan => documentSpan.SourceSpan.Start).ToImmutableArray(); + var expectedDocumentSpans = expectedTarget.DocumentSpans.OrderBy(documentSpan => documentSpan.SourceSpan.Start).ToImmutableArray(); + Assert.Equal(expectedDocumentSpans.Length, actualDocumentSpans.Length); + for (var i = 0; i < actualDocumentSpans.Length; i++) + { + Assert.Equal(expectedDocumentSpans[i].SourceSpan, actualDocumentSpans[i].SourceSpan); + Assert.Equal(expectedDocumentSpans[i].Document.FilePath, actualDocumentSpans[i].Document.FilePath); + } + } + } + + /// + /// Project of markup1 is referencing project of markup2 + /// + private static async Task VerifyInDifferentProjectsAsync( + (string markupInProject1, string languageName) markup1, + (string markupInProject2, string languageName) markup2, + TestInheritanceMemberItem[] memberItemsInMarkup1, + TestInheritanceMemberItem[] memberItemsInMarkup2) + { + var workspaceFile = + $@" + + + Assembly2 + + {markup1.markupInProject1} + + + + + {markup2.markupInProject2} + + +"; + + var cancellationToken = CancellationToken.None; + using var testWorkspace = TestWorkspace.Create( + workspaceFile, + composition: EditorTestCompositions.EditorFeatures); + + var testHostDocument1 = testWorkspace.Documents.Single(doc => doc.Project.AssemblyName.Equals("Assembly1")); + var testHostDocument2 = testWorkspace.Documents.Single(doc => doc.Project.AssemblyName.Equals("Assembly2")); + await VerifyTestMemberInDocumentAsync(testWorkspace, testHostDocument1, memberItemsInMarkup1, cancellationToken).ConfigureAwait(false); + await VerifyTestMemberInDocumentAsync(testWorkspace, testHostDocument2, memberItemsInMarkup2, cancellationToken).ConfigureAwait(false); + } + + private class TestInheritanceMemberItem + { + public readonly int LineNumber; + public readonly string MemberName; + public readonly ImmutableArray Targets; + + public TestInheritanceMemberItem( + int lineNumber, + string memberName, + ImmutableArray targets) + { + LineNumber = lineNumber; + MemberName = memberName; + Targets = targets; + } + } + + private class TargetInfo + { + public readonly string TargetSymbolDisplayName; + public readonly string? LocationTag; + public readonly InheritanceRelationship Relationship; + public readonly bool InMetadata; + + public TargetInfo( + string targetSymbolDisplayName, + string locationTag, + InheritanceRelationship relationship) + { + TargetSymbolDisplayName = targetSymbolDisplayName; + LocationTag = locationTag; + Relationship = relationship; + InMetadata = false; + } + + public TargetInfo( + string targetSymbolDisplayName, + InheritanceRelationship relationship, + bool inMetadata) + { + TargetSymbolDisplayName = targetSymbolDisplayName; + Relationship = relationship; + InMetadata = inMetadata; + LocationTag = null; + } + } + + private class TestInheritanceTargetItem + { + public readonly string TargetSymbolName; + public readonly InheritanceRelationship RelationshipToMember; + public readonly ImmutableArray DocumentSpans; + public readonly bool IsInMetadata; + + public TestInheritanceTargetItem( + string targetSymbolName, + InheritanceRelationship relationshipToMember, + ImmutableArray documentSpans, + bool isInMetadata) + { + TargetSymbolName = targetSymbolName; + RelationshipToMember = relationshipToMember; + DocumentSpans = documentSpans; + IsInMetadata = isInMetadata; + } + + public static TestInheritanceTargetItem Create( + TargetInfo targetInfo, + TestWorkspace testWorkspace) + { + if (targetInfo.InMetadata) + { + return new TestInheritanceTargetItem( + targetInfo.TargetSymbolDisplayName, + targetInfo.Relationship, + ImmutableArray.Empty, + isInMetadata: true); + } + else + { + using var _ = ArrayBuilder.GetInstance(out var builder); + // If the target is not in metadata, there must be a location tag to give the span! + Assert.True(targetInfo.LocationTag != null); + foreach (var testHostDocument in testWorkspace.Documents) + { + if (targetInfo.LocationTag != null) + { + var annotatedSpans = testHostDocument.AnnotatedSpans; + if (annotatedSpans.TryGetValue(targetInfo.LocationTag, out var spans)) + { + var document = testWorkspace.CurrentSolution.GetRequiredDocument(testHostDocument.Id); + builder.AddRange(spans.Select(span => new DocumentSpan(document, span))); + } + } + } + + return new TestInheritanceTargetItem( + targetInfo.TargetSymbolDisplayName, + targetInfo.Relationship, + builder.ToImmutable(), + isInMetadata: false); + } + } + } + + #endregion + + #region TestsForCSharp + + [Fact] + public Task TestCSharpClassWithErrorBaseType() + { + var markup = @" +public class Bar : SomethingUnknown +{ +}"; + return VerifyNoItemForDocumentAsync(markup, LanguageNames.CSharp); + } + + [Fact] + public Task TestCSharpReferencingMetadata() + { + var markup = @" +using System.Collections; +public class Bar : IEnumerable +{ + public IEnumerator GetEnumerator () { return null }; +}"; + var itemForBar = new TestInheritanceMemberItem( + lineNumber: 3, + memberName: "class Bar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "interface IEnumerable", + relationship: InheritanceRelationship.ImplementedInterface, + inMetadata: true))); + + var itemForGetEnumerator = new TestInheritanceMemberItem( + lineNumber: 5, + memberName: "IEnumerator Bar.GetEnumerator()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "IEnumerator IEnumerable.GetEnumerator()", + relationship: InheritanceRelationship.ImplementedMember, + inMetadata: true))); + + return VerifyInSingleDocumentAsync(markup, LanguageNames.CSharp, itemForBar, itemForGetEnumerator); + } + + [Fact] + public Task TestCSharpClassImplementingInterface() + { + var markup = @" +interface {|target1:IBar|} { } +public class {|target2:Bar|} : IBar +{ +} + "; + + var itemOnLine2 = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "interface IBar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "class Bar", + locationTag: "target2", + relationship: InheritanceRelationship.ImplementingType))); + + var itemOnLine3 = new TestInheritanceMemberItem( + lineNumber: 3, + memberName: "class Bar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "interface IBar", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementedInterface))); + + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.CSharp, + itemOnLine2, + itemOnLine3); + } + + [Fact] + public Task TestCSharpInterfaceImplementingInterface() + { + var markup = @" + interface {|target1:IBar|} { } + interface {|target2:IBar2|} : IBar { } + "; + + var itemOnLine2 = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "interface IBar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "interface IBar2", + locationTag: "target2", + relationship: InheritanceRelationship.ImplementingType)) + ); + var itemOnLine3 = new TestInheritanceMemberItem( + lineNumber: 3, + memberName: "interface IBar2", + targets: ImmutableArray.Empty + .Add(new TargetInfo( + targetSymbolDisplayName: "interface IBar", + locationTag: "target1", + relationship: InheritanceRelationship.InheritedInterface)) + ); + + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.CSharp, + itemOnLine2, + itemOnLine3); + } + + [Fact] + public Task TestCSharpClassInheritsClass() + { + var markup = @" + class {|target2:A|} { } + class {|target1:B|} : A { } + "; + + var itemOnLine2 = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "class A", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "class B", + locationTag: "target1", + relationship: InheritanceRelationship.DerivedType)) + ); + var itemOnLine3 = new TestInheritanceMemberItem( + lineNumber: 3, + memberName: "class B", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "class A", + locationTag: "target2", + relationship: InheritanceRelationship.BaseType)) + ); + + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.CSharp, + itemOnLine2, + itemOnLine3); + } + + [Theory] + [InlineData("class")] + [InlineData("struct")] + [InlineData("enum")] + [InlineData("interface")] + public Task TestCSharpTypeWithoutBaseType(string typeName) + { + var markup = $@" + public {typeName} Bar + {{ + }}"; + return VerifyNoItemForDocumentAsync(markup, LanguageNames.CSharp); + } + + [Theory] + [InlineData("public Bar() { }")] + [InlineData("public static void Bar3() { }")] + [InlineData("public static void ~Bar() { }")] + [InlineData("public static Bar operator +(Bar a, Bar b) => new Bar();")] + public Task TestCSharpSpecialMember(string memberDeclaration) + { + var markup = $@" + public abstract class {{|target1:Bar1|}} + {{}} + public class Bar : Bar1 + {{ + {{|{SearchAreaTag}:{memberDeclaration}|}} + }}"; + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.CSharp, + new TestInheritanceMemberItem( + lineNumber: 4, + memberName: "class Bar", + targets: ImmutableArray.Create( + new TargetInfo( + targetSymbolDisplayName: "class Bar1", + locationTag: "target1", + relationship: InheritanceRelationship.BaseType)))); + } + + [Fact] + public Task TestCSharpEventDeclaration() + { + var markup = @" + using System; + interface {|target2:IBar|} + { + event EventHandler {|target4:e|}; + } + public class {|target1:Bar|} : IBar + { + public event EventHandler {|target3:e|} + { + add {} remove {} + } + }"; + var itemForIBar = new TestInheritanceMemberItem( + lineNumber: 3, + memberName: "interface IBar", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "class Bar", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementingType))); + + var itemForBar = new TestInheritanceMemberItem( + lineNumber: 7, + memberName: "class Bar", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "interface IBar", + locationTag: "target2", + relationship: InheritanceRelationship.ImplementedInterface))); + + var itemForEventInInterface = new TestInheritanceMemberItem( + lineNumber: 5, + memberName: "event EventHandler IBar.e", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "event EventHandler Bar.e", + locationTag: "target3", + relationship: InheritanceRelationship.ImplementingMember))); + + var itemForEventInClass = new TestInheritanceMemberItem( + lineNumber: 9, + memberName: "event EventHandler Bar.e", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "event EventHandler IBar.e", + locationTag: "target4", + relationship: InheritanceRelationship.ImplementedMember))); + + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.CSharp, + itemForIBar, + itemForBar, + itemForEventInInterface, + itemForEventInClass); + } + + [Fact] + public Task TestCSharpEventFieldDeclarations() + { + var markup = @"using System; + interface {|target2:IBar|} + { + event EventHandler {|target5:e1|}, {|target6:e2|}; + } + public class {|target1:Bar|} : IBar + { + public event EventHandler {|target3:e1|}, {|target4:e2|}; + }"; + var itemForIBar = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "interface IBar", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "class Bar", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementingType))); + + var itemForBar = new TestInheritanceMemberItem( + lineNumber: 6, + memberName: "class Bar", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "interface IBar", + locationTag: "target2", + relationship: InheritanceRelationship.ImplementedInterface))); + + var itemForE1InInterface = new TestInheritanceMemberItem( + lineNumber: 4, + memberName: "event EventHandler IBar.e1", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "event EventHandler Bar.e1", + locationTag: "target3", + relationship: InheritanceRelationship.ImplementingMember))); + + var itemForE2InInterface = new TestInheritanceMemberItem( + lineNumber: 4, + memberName: "event EventHandler IBar.e2", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "event EventHandler Bar.e2", + locationTag: "target4", + relationship: InheritanceRelationship.ImplementingMember))); + + var itemForE1InClass = new TestInheritanceMemberItem( + lineNumber: 8, + memberName: "event EventHandler Bar.e1", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "event EventHandler IBar.e1", + locationTag: "target5", + relationship: InheritanceRelationship.ImplementedMember))); + + var itemForE2InClass = new TestInheritanceMemberItem( + lineNumber: 8, + memberName: "event EventHandler Bar.e2", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "event EventHandler IBar.e2", + locationTag: "target6", + relationship: InheritanceRelationship.ImplementedMember))); + + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.CSharp, + itemForIBar, + itemForBar, + itemForE1InInterface, + itemForE2InInterface, + itemForE1InClass, + itemForE2InClass); + } + + [Fact] + public Task TestCSharpInterfaceMembers() + { + var markup = @"using System; + interface {|target1:IBar|} + { + void {|target4:Foo|}(); + int {|target6:Poo|} { get; set; } + event EventHandler {|target8:Eoo|}; + int {|target9:this|}[int i] { get; set; } + } + public class {|target2:Bar|} : IBar + { + public void {|target3:Foo|}() { } + public int {|target5:Poo|} { get; set; } + public event EventHandler {|target7:Eoo|}; + public int {|target10:this|}[int i] { get => 1; set { } } + }"; + var itemForEooInClass = new TestInheritanceMemberItem( + lineNumber: 13, + memberName: "event EventHandler Bar.Eoo", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "event EventHandler IBar.Eoo", + locationTag: "target8", + relationship: InheritanceRelationship.ImplementedMember)) + ); + + var itemForEooInInterface = new TestInheritanceMemberItem( + lineNumber: 6, + memberName: "event EventHandler IBar.Eoo", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "event EventHandler Bar.Eoo", + locationTag: "target7", + relationship: InheritanceRelationship.ImplementingMember)) + ); + + var itemForPooInInterface = new TestInheritanceMemberItem( + lineNumber: 5, + memberName: "int IBar.Poo { get; set; }", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "int Bar.Poo { get; set; }", + locationTag: "target5", + relationship: InheritanceRelationship.ImplementingMember)) + ); + + var itemForPooInClass = new TestInheritanceMemberItem( + lineNumber: 12, + memberName: "int Bar.Poo { get; set; }", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "int IBar.Poo { get; set; }", + locationTag: "target6", + relationship: InheritanceRelationship.ImplementedMember)) + ); + + var itemForFooInInterface = new TestInheritanceMemberItem( + lineNumber: 4, + memberName: "void IBar.Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "void Bar.Foo()", + locationTag: "target3", + relationship: InheritanceRelationship.ImplementingMember)) + ); + + var itemForFooInClass = new TestInheritanceMemberItem( + lineNumber: 11, + memberName: "void Bar.Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "void IBar.Foo()", + locationTag: "target4", + relationship: InheritanceRelationship.ImplementedMember)) + ); + + var itemForIBar = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "interface IBar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "class Bar", + locationTag: "target2", + relationship: InheritanceRelationship.ImplementingType)) + ); + + var itemForBar = new TestInheritanceMemberItem( + lineNumber: 9, + memberName: "class Bar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "interface IBar", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementedInterface)) + ); + + var itemForIndexerInClass = new TestInheritanceMemberItem( + lineNumber: 14, + memberName: "int Bar.this[int] { get; set; }", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "int IBar.this[int] { get; set; }", + locationTag: "target9", + relationship: InheritanceRelationship.ImplementedMember)) + ); + + var itemForIndexerInInterface = new TestInheritanceMemberItem( + lineNumber: 7, + memberName: "int IBar.this[int] { get; set; }", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "int Bar.this[int] { get; set; }", + locationTag: "target10", + relationship: InheritanceRelationship.ImplementingMember)) + ); + + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.CSharp, + itemForEooInClass, + itemForEooInInterface, + itemForPooInInterface, + itemForPooInClass, + itemForFooInInterface, + itemForFooInClass, + itemForIBar, + itemForBar, + itemForIndexerInInterface, + itemForIndexerInClass); + } + + [Theory] + [InlineData("abstract")] + [InlineData("virtual")] + public Task TestCSharpAbstractClassMembers(string modifier) + { + var markup = $@"using System; + public abstract class {{|target2:Bar|}} + {{ + public {modifier} void {{|target4:Foo|}}(); + public {modifier} int {{|target6:Poo|}} {{ get; set; }} + public {modifier} event EventHandler {{|target8:Eoo|}}; + }} + public class {{|target1:Bar2|}} : Bar + {{ + public override void {{|target3:Foo|}}() {{ }} + public override int {{|target5:Poo|}} {{ get; set; }} + public override event EventHandler {{|target7:Eoo|}}; + }} + "; + + var itemForEooInClass = new TestInheritanceMemberItem( + lineNumber: 12, + memberName: "override event EventHandler Bar2.Eoo", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: $"{modifier} event EventHandler Bar.Eoo", + locationTag: "target8", + relationship: InheritanceRelationship.OverriddenMember))); + + var itemForEooInAbstractClass = new TestInheritanceMemberItem( + lineNumber: 6, + memberName: $"{modifier} event EventHandler Bar.Eoo", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "override event EventHandler Bar2.Eoo", + locationTag: "target7", + relationship: InheritanceRelationship.OverridingMember))); + + var itemForPooInClass = new TestInheritanceMemberItem( + lineNumber: 11, + memberName: "override int Bar2.Poo { get; set; }", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: $"{modifier} int Bar.Poo {{ get; set; }}", + locationTag: "target6", + relationship: InheritanceRelationship.OverriddenMember))); + + var itemForPooInAbstractClass = new TestInheritanceMemberItem( + lineNumber: 5, + memberName: $"{modifier} int Bar.Poo {{ get; set; }}", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "override int Bar2.Poo { get; set; }", + locationTag: "target5", + relationship: InheritanceRelationship.OverridingMember))); + + var itemForFooInAbstractClass = new TestInheritanceMemberItem( + lineNumber: 4, + memberName: $"{modifier} void Bar.Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "override void Bar2.Foo()", + locationTag: "target3", + relationship: InheritanceRelationship.OverridingMember))); + + var itemForFooInClass = new TestInheritanceMemberItem( + lineNumber: 10, + memberName: "override void Bar2.Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: $"{modifier} void Bar.Foo()", + locationTag: "target4", + relationship: InheritanceRelationship.OverriddenMember))); + + var itemForBar = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "class Bar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "class Bar2", + locationTag: "target1", + relationship: InheritanceRelationship.DerivedType))); + + var itemForBar2 = new TestInheritanceMemberItem( + lineNumber: 8, + memberName: "class Bar2", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "class Bar", + locationTag: "target2", + relationship: InheritanceRelationship.BaseType))); + + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.CSharp, + itemForBar, + itemForBar2, + itemForFooInAbstractClass, + itemForFooInClass, + itemForPooInClass, + itemForPooInAbstractClass, + itemForEooInClass, + itemForEooInAbstractClass); + } + + [Theory] + [CombinatorialData] + public Task TestCSharpOverrideMemberCanFindImplementingInterface(bool testDuplicate) + { + var markup1 = @"using System; + public interface {|target4:IBar|} + { + void {|target6:Foo|}(); + } + public class {|target1:Bar1|} : IBar + { + public virtual void {|target2:Foo|}() { } + } + public class {|target5:Bar2|} : Bar1 + { + public override void {|target3:Foo|}() { } + }"; + + var markup2 = @"using System; + public interface {|target4:IBar|} + { + void {|target6:Foo|}(); + } + public class {|target1:Bar1|} : IBar + { + public virtual void {|target2:Foo|}() { } + } + public class {|target5:Bar2|} : Bar1, IBar + { + public override void {|target3:Foo|}() { } + }"; + + var itemForIBar = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "interface IBar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "class Bar1", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementingType), + new TargetInfo( + targetSymbolDisplayName: "class Bar2", + locationTag: "target5", + relationship: InheritanceRelationship.ImplementingType))); + + var itemForFooInIBar = new TestInheritanceMemberItem( + lineNumber: 4, + memberName: "void IBar.Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "virtual void Bar1.Foo()", + locationTag: "target2", + relationship: InheritanceRelationship.ImplementingMember), + new TargetInfo( + targetSymbolDisplayName: "override void Bar2.Foo()", + locationTag: "target3", + relationship: InheritanceRelationship.ImplementingMember))); + + var itemForBar1 = new TestInheritanceMemberItem( + lineNumber: 6, + memberName: "class Bar1", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "interface IBar", + locationTag: "target4", + relationship: InheritanceRelationship.ImplementedInterface), + new TargetInfo( + targetSymbolDisplayName: "class Bar2", + locationTag: "target5", + relationship: InheritanceRelationship.DerivedType))); + + var itemForFooInBar1 = new TestInheritanceMemberItem( + lineNumber: 8, + memberName: "virtual void Bar1.Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "void IBar.Foo()", + locationTag: "target6", + relationship: InheritanceRelationship.ImplementedMember), + new TargetInfo( + targetSymbolDisplayName: "override void Bar2.Foo()", + locationTag: "target3", + relationship: InheritanceRelationship.OverridingMember))); + + var itemForBar2 = new TestInheritanceMemberItem( + lineNumber: 10, + memberName: "class Bar2", + targets: ImmutableArray.Create( + new TargetInfo( + targetSymbolDisplayName: "class Bar1", + locationTag: "target1", + relationship: InheritanceRelationship.BaseType), + new TargetInfo( + targetSymbolDisplayName: "interface IBar", + locationTag: "target4", + relationship: InheritanceRelationship.ImplementedInterface))); + + var itemForFooInBar2 = new TestInheritanceMemberItem( + lineNumber: 12, + memberName: "override void Bar2.Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "void IBar.Foo()", + locationTag: "target6", + relationship: InheritanceRelationship.ImplementedMember), + new TargetInfo( + targetSymbolDisplayName: "virtual void Bar1.Foo()", + locationTag: "target2", + relationship: InheritanceRelationship.OverriddenMember))); + + return VerifyInSingleDocumentAsync( + testDuplicate ? markup2 : markup1, + LanguageNames.CSharp, + itemForIBar, + itemForFooInIBar, + itemForBar1, + itemForFooInBar1, + itemForBar2, + itemForFooInBar2); + } + + [Fact] + public Task TestCSharpFindGenericsBaseType() + { + var lessThanToken = SecurityElement.Escape("<"); + var greaterThanToken = SecurityElement.Escape(">"); + var markup = $@" + public interface {{|target2:IBar|}}{lessThanToken}T{greaterThanToken} + {{ + void {{|target4:Foo|}}(); + }} + + public class {{|target1:Bar2|}} : IBar{lessThanToken}int{greaterThanToken}, IBar{lessThanToken}string{greaterThanToken} + {{ + public void {{|target3:Foo|}}(); + }}"; + + var itemForIBar = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "interface IBar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "class Bar2", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementingType))); + + var itemForFooInIBar = new TestInheritanceMemberItem( + lineNumber: 4, + memberName: "void IBar.Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "void Bar2.Foo()", + locationTag: "target3", + relationship: InheritanceRelationship.ImplementingMember))); + + // Only have one IBar item + var itemForBar2 = new TestInheritanceMemberItem( + lineNumber: 7, + memberName: "class Bar2", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "interface IBar", + locationTag: "target2", + relationship: InheritanceRelationship.ImplementedInterface))); + + // Only have one IBar.Foo item + var itemForFooInBar2 = new TestInheritanceMemberItem( + lineNumber: 9, + memberName: "void Bar2.Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "void IBar.Foo()", + locationTag: "target4", + relationship: InheritanceRelationship.ImplementedMember))); + + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.CSharp, + itemForIBar, + itemForFooInIBar, + itemForBar2, + itemForFooInBar2); + } + #endregion + + #region TestsForVisualBasic + + [Fact] + public Task TestVisualBasicWithErrorBaseType() + { + var markup = @" + Namespace MyNamespace + Public Class Bar + Implements SomethingNotExist + End Class + End Namespace"; + + return VerifyNoItemForDocumentAsync(markup, LanguageNames.VisualBasic); + } + + [Fact] + public Task TestVisualBasicReferencingMetadata() + { + var markup = @" + Namespace MyNamespace + Public Class Bar + Implements System.Collections.IEnumerable + Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator + Throw New NotImplementedException() + End Function + End Class + End Namespace"; + var itemForBar = new TestInheritanceMemberItem( + lineNumber: 3, + memberName: "Class Bar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Interface IEnumerable", + relationship: InheritanceRelationship.ImplementedInterface, + inMetadata: true))); + + var itemForGetEnumerator = new TestInheritanceMemberItem( + lineNumber: 5, + memberName: "Function Bar.GetEnumerator() As IEnumerator", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Function IEnumerable.GetEnumerator() As IEnumerator", + relationship: InheritanceRelationship.ImplementedMember, + inMetadata: true))); + + return VerifyInSingleDocumentAsync(markup, LanguageNames.VisualBasic, itemForBar, itemForGetEnumerator); + } + + [Fact] + public Task TestVisualBasicClassImplementingInterface() + { + var markup = @" + Interface {|target2:IBar|} + End Interface + Class {|target1:Bar|} + Implements IBar + End Class"; + var itemForIBar = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "Interface IBar", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Class Bar", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementingType))); + + var itemForBar = new TestInheritanceMemberItem( + lineNumber: 4, + memberName: "Class Bar", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Interface IBar", + locationTag: "target2", + relationship: InheritanceRelationship.ImplementedInterface))); + + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.VisualBasic, + itemForIBar, + itemForBar); + } + + [Fact] + public Task TestVisualBasicInterfaceImplementingInterface() + { + var markup = @" + Interface {|target2:IBar2|} + End Interface + Interface {|target1:IBar|} + Inherits IBar2 + End Interface"; + + var itemForIBar2 = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "Interface IBar2", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Interface IBar", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementingType))); + + var itemForIBar = new TestInheritanceMemberItem( + lineNumber: 4, + memberName: "Interface IBar", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Interface IBar2", + locationTag: "target2", + relationship: InheritanceRelationship.InheritedInterface))); + return VerifyInSingleDocumentAsync(markup, LanguageNames.VisualBasic, itemForIBar2, itemForIBar); + } + + [Fact] + public Task TestVisualBasicClassInheritsClass() + { + var markup = @" + Class {|target2:Bar2|} + End Class + Class {|target1:Bar|} + Inherits Bar2 + End Class"; + + var itemForBar2 = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "Class Bar2", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Class Bar", + locationTag: "target1", + relationship: InheritanceRelationship.DerivedType))); + + var itemForBar = new TestInheritanceMemberItem( + lineNumber: 4, + memberName: "Class Bar", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Class Bar2", + locationTag: "target2", + relationship: InheritanceRelationship.BaseType))); + return VerifyInSingleDocumentAsync(markup, LanguageNames.VisualBasic, itemForBar2, itemForBar); + } + + [Theory] + [InlineData("Class")] + [InlineData("Structure")] + [InlineData("Enum")] + [InlineData("Interface")] + public Task TestVisualBasicTypeWithoutBaseType(string typeName) + { + var markup = $@" + {typeName} Bar + End {typeName}"; + + return VerifyNoItemForDocumentAsync(markup, LanguageNames.VisualBasic); + } + + [Fact] + public Task TestVisualBasicMetadataInterface() + { + var markup = @" + Imports System.Collections + Class Bar + Implements IEnumerable + End Class"; + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.VisualBasic, + new TestInheritanceMemberItem( + lineNumber: 3, + memberName: "Class Bar", + targets: ImmutableArray.Create( + new TargetInfo( + targetSymbolDisplayName: "Interface IEnumerable", + relationship: InheritanceRelationship.ImplementedInterface, + inMetadata: true)))); + } + + [Fact] + public Task TestVisualBasicEventStatement() + { + var markup = @" + Interface {|target2:IBar|} + Event {|target4:e|} As EventHandler + End Interface + Class {|target1:Bar|} + Implements IBar + Public Event {|target3:e|} As EventHandler Implements IBar.e + End Class"; + + var itemForIBar = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "Interface IBar", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Class Bar", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementingType))); + + var itemForBar = new TestInheritanceMemberItem( + lineNumber: 5, + memberName: "Class Bar", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Interface IBar", + locationTag: "target2", + relationship: InheritanceRelationship.ImplementedInterface))); + + var itemForEventInInterface = new TestInheritanceMemberItem( + lineNumber: 3, + memberName: "Event IBar.e As EventHandler", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Event Bar.e As EventHandler", + locationTag: "target3", + relationship: InheritanceRelationship.ImplementingMember))); + + var itemForEventInClass = new TestInheritanceMemberItem( + lineNumber: 7, + memberName: "Event Bar.e As EventHandler", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Event IBar.e As EventHandler", + locationTag: "target4", + relationship: InheritanceRelationship.ImplementedMember))); + + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.VisualBasic, + itemForIBar, + itemForBar, + itemForEventInInterface, + itemForEventInClass); + } + + [Fact] + public Task TestVisualBasicEventBlock() + { + var markup = @" + Interface {|target2:IBar|} + Event {|target4:e|} As EventHandler + End Interface + Class {|target1:Bar|} + Implements IBar + Public Custom Event {|target3:e|} As EventHandler Implements IBar.e + End Event + End Class"; + var itemForIBar = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "Interface IBar", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Class Bar", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementingType))); + + var itemForBar = new TestInheritanceMemberItem( + lineNumber: 5, + memberName: "Class Bar", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Interface IBar", + locationTag: "target2", + relationship: InheritanceRelationship.ImplementedInterface))); + + var itemForEventInInterface = new TestInheritanceMemberItem( + lineNumber: 3, + memberName: "Event IBar.e As EventHandler", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Event Bar.e As EventHandler", + locationTag: "target3", + relationship: InheritanceRelationship.ImplementingMember))); + + var itemForEventInClass = new TestInheritanceMemberItem( + lineNumber: 7, + memberName: "Event Bar.e As EventHandler", + ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Event IBar.e As EventHandler", + locationTag: "target4", + relationship: InheritanceRelationship.ImplementedMember))); + + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.VisualBasic, + itemForIBar, + itemForBar, + itemForEventInInterface, + itemForEventInClass); + } + + [Fact] + public Task TestVisualBasicInterfaceMembers() + { + var markup = @" + Interface {|target2:IBar|} + Property {|target4:Poo|} As Integer + Function {|target6:Foo|}() As Integer + End Interface + + Class {|target1:Bar|} + Implements IBar + Public Property {|target3:Poo|} As Integer Implements IBar.Poo + Get + Return 1 + End Get + Set(value As Integer) + End Set + End Property + Public Function {|target5:Foo|}() As Integer Implements IBar.Foo + Return 1 + End Function + End Class"; + var itemForIBar = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "Interface IBar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Class Bar", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementingType))); + + var itemForBar = new TestInheritanceMemberItem( + lineNumber: 7, + memberName: "Class Bar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Interface IBar", + locationTag: "target2", + relationship: InheritanceRelationship.ImplementedInterface))); + + var itemForPooInInterface = new TestInheritanceMemberItem( + lineNumber: 3, + memberName: "Property IBar.Poo As Integer", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Property Bar.Poo As Integer", + locationTag: "target3", + relationship: InheritanceRelationship.ImplementingMember))); + + var itemForPooInClass = new TestInheritanceMemberItem( + lineNumber: 9, + memberName: "Property Bar.Poo As Integer", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Property IBar.Poo As Integer", + locationTag: "target4", + relationship: InheritanceRelationship.ImplementedMember))); + + var itemForFooInInterface = new TestInheritanceMemberItem( + lineNumber: 4, + memberName: "Function IBar.Foo() As Integer", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Function Bar.Foo() As Integer", + locationTag: "target5", + relationship: InheritanceRelationship.ImplementingMember))); + + var itemForFooInClass = new TestInheritanceMemberItem( + lineNumber: 16, + memberName: "Function Bar.Foo() As Integer", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Function IBar.Foo() As Integer", + locationTag: "target6", + relationship: InheritanceRelationship.ImplementedMember))); + + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.VisualBasic, + itemForIBar, + itemForBar, + itemForPooInInterface, + itemForPooInClass, + itemForFooInInterface, + itemForFooInClass); + } + + [Fact] + public Task TestVisualBasicMustInheritClassMember() + { + var markup = @" + MustInherit Class {|target2:Bar1|} + Public MustOverride Sub {|target4:Foo|}() + End Class + + Class {|target1:Bar|} + Inherits Bar1 + Public Overrides Sub {|target3:Foo|}() + End Sub + End Class"; + var itemForBar1 = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "Class Bar1", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: $"Class Bar", + locationTag: "target1", + relationship: InheritanceRelationship.DerivedType))); + + var itemForBar = new TestInheritanceMemberItem( + lineNumber: 6, + memberName: "Class Bar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Class Bar1", + locationTag: "target2", + relationship: InheritanceRelationship.BaseType))); + + var itemForFooInBar1 = new TestInheritanceMemberItem( + lineNumber: 3, + memberName: "MustOverride Sub Bar1.Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Overrides Sub Bar.Foo()", + locationTag: "target3", + relationship: InheritanceRelationship.OverridingMember))); + + var itemForFooInBar = new TestInheritanceMemberItem( + lineNumber: 8, + memberName: "Overrides Sub Bar.Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "MustOverride Sub Bar1.Foo()", + locationTag: "target4", + relationship: InheritanceRelationship.OverriddenMember))); + + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.VisualBasic, + itemForBar1, + itemForBar, + itemForFooInBar1, + itemForFooInBar); + } + + [Theory] + [CombinatorialData] + public Task TestVisualBasicOverrideMemberCanFindImplementingInterface(bool testDuplicate) + { + var markup1 = @" + Interface {|target4:IBar|} + Sub {|target6:Foo|}() + End Interface + + Class {|target1:Bar1|} + Implements IBar + Public Overridable Sub {|target2:Foo|}() Implements IBar.Foo + End Sub + End Class + + Class {|target5:Bar2|} + Inherits Bar1 + Public Overrides Sub {|target3:Foo|}() + End Sub + End Class"; + + var markup2 = @" + Interface {|target4:IBar|} + Sub {|target6:Foo|}() + End Interface + + Class {|target1:Bar1|} + Implements IBar + Public Overridable Sub {|target2:Foo|}() Implements IBar.Foo + End Sub + End Class + + Class {|target5:Bar2|} + Inherits Bar1 + Public Overrides Sub {|target3:Foo|}() + End Sub + End Class"; + var itemForIBar = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "Interface IBar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Class Bar1", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementingType), + new TargetInfo( + targetSymbolDisplayName: "Class Bar2", + locationTag: "target5", + relationship: InheritanceRelationship.ImplementingType))); + + var itemForFooInIBar = new TestInheritanceMemberItem( + lineNumber: 3, + memberName: "Sub IBar.Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Overridable Sub Bar1.Foo()", + locationTag: "target2", + relationship: InheritanceRelationship.ImplementingMember), + new TargetInfo( + targetSymbolDisplayName: "Overrides Sub Bar2.Foo()", + locationTag: "target3", + relationship: InheritanceRelationship.ImplementingMember))); + + var itemForBar1 = new TestInheritanceMemberItem( + lineNumber: 6, + memberName: "Class Bar1", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Interface IBar", + locationTag: "target4", + relationship: InheritanceRelationship.ImplementedInterface), + new TargetInfo( + targetSymbolDisplayName: "Class Bar2", + locationTag: "target5", + relationship: InheritanceRelationship.DerivedType))); + + var itemForFooInBar1 = new TestInheritanceMemberItem( + lineNumber: 8, + memberName: "Overridable Sub Bar1.Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Sub IBar.Foo()", + locationTag: "target6", + relationship: InheritanceRelationship.ImplementedMember), + new TargetInfo( + targetSymbolDisplayName: "Overrides Sub Bar2.Foo()", + locationTag: "target3", + relationship: InheritanceRelationship.OverridingMember))); + + var itemForBar2 = new TestInheritanceMemberItem( + lineNumber: 12, + memberName: "Class Bar2", + targets: ImmutableArray.Create( + new TargetInfo( + targetSymbolDisplayName: "Class Bar1", + locationTag: "target1", + relationship: InheritanceRelationship.BaseType), + new TargetInfo( + targetSymbolDisplayName: "Interface IBar", + locationTag: "target4", + relationship: InheritanceRelationship.ImplementedInterface))); + + var itemForFooInBar2 = new TestInheritanceMemberItem( + lineNumber: 14, + memberName: "Overrides Sub Bar2.Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Sub IBar.Foo()", + locationTag: "target6", + relationship: InheritanceRelationship.ImplementedMember), + new TargetInfo( + targetSymbolDisplayName: "Overridable Sub Bar1.Foo()", + locationTag: "target2", + relationship: InheritanceRelationship.OverriddenMember))); + + return VerifyInSingleDocumentAsync( + testDuplicate ? markup2 : markup1, + LanguageNames.VisualBasic, + itemForIBar, + itemForFooInIBar, + itemForBar1, + itemForFooInBar1, + itemForBar2, + itemForFooInBar2); + } + + [Fact] + public Task TestVisualBasicFindGenericsBaseType() + { + var markup = @" + Public Interface {|target5:IBar|}(Of T) + Sub {|target6:Foo|}() + End Interface + + Public Class {|target1:Bar|} + Implements IBar(Of Integer) + Implements IBar(Of String) + + Public Sub {|target3:Foo|}() Implements IBar(Of Integer).Foo + Throw New NotImplementedException() + End Sub + + Private Sub {|target4:IBar_Foo|}() Implements IBar(Of String).Foo + Throw New NotImplementedException() + End Sub + End Class"; + + var itemForIBar = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "Interface IBar(Of T)", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Class Bar", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementingType))); + + var itemForFooInIBar = new TestInheritanceMemberItem( + lineNumber: 3, + memberName: "Sub IBar(Of T).Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Sub Bar.Foo()", + locationTag: "target3", + relationship: InheritanceRelationship.ImplementingMember), + new TargetInfo( + targetSymbolDisplayName: "Sub Bar.IBar_Foo()", + locationTag: "target4", + relationship: InheritanceRelationship.ImplementingMember))); + + var itemForBar = new TestInheritanceMemberItem( + lineNumber: 6, + memberName: "Class Bar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Interface IBar(Of T)", + locationTag: "target5", + relationship: InheritanceRelationship.ImplementedInterface))); + + var itemForFooInBar = new TestInheritanceMemberItem( + lineNumber: 10, + memberName: "Sub Bar.Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Sub IBar(Of T).Foo()", + locationTag: "target6", + relationship: InheritanceRelationship.ImplementedMember))); + + var itemForIBar_FooInBar = new TestInheritanceMemberItem( + lineNumber: 14, + memberName: "Sub Bar.IBar_Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Sub IBar(Of T).Foo()", + locationTag: "target6", + relationship: InheritanceRelationship.ImplementedMember))); + + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.VisualBasic, + itemForIBar, + itemForFooInIBar, + itemForBar, + itemForFooInBar, + itemForIBar_FooInBar); + } + + #endregion + + [Fact] + public Task TestCSharpProjectReferencingVisualBasicProject() + { + var markup1 = @" + using MyNamespace; + namespace BarNs + { + public class {|target2:Bar|} : IBar + { + public void {|target4:Foo|}() { } + } + }"; + + var markup2 = @" + Namespace MyNamespace + Public Interface {|target1:IBar|} + Sub {|target3:Foo|}() + End Interface + End Namespace"; + + var itemForBar = new TestInheritanceMemberItem( + lineNumber: 5, + memberName: "class Bar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Interface IBar", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementedInterface))); + + var itemForFooInMarkup1 = new TestInheritanceMemberItem( + lineNumber: 7, + memberName: "void Bar.Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Sub IBar.Foo()", + locationTag: "target3", + relationship: InheritanceRelationship.ImplementedMember))); + + var itemForIBar = new TestInheritanceMemberItem( + lineNumber: 3, + memberName: "Interface IBar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "class Bar", + locationTag: "target2", + relationship: InheritanceRelationship.ImplementingType))); + + var itemForFooInMarkup2 = new TestInheritanceMemberItem( + lineNumber: 4, + memberName: "Sub IBar.Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "void Bar.Foo()", + locationTag: "target4", + relationship: InheritanceRelationship.ImplementingMember))); + + return VerifyInDifferentProjectsAsync( + (markup1, LanguageNames.CSharp), + (markup2, LanguageNames.VisualBasic), + new[] { itemForBar, itemForFooInMarkup1 }, + new[] { itemForIBar, itemForFooInMarkup2 }); + } + + [Fact] + public Task TestVisualBasicProjectReferencingCSharpProject() + { + var markup1 = @" + Imports BarNs + Namespace MyNamespace + Public Class {|target2:Bar44|} + Implements IBar + + Public Sub {|target4:Foo|}() Implements IBar.Foo + End Sub + End Class + End Namespace"; + + var markup2 = @" + namespace BarNs + { + public interface {|target1:IBar|} + { + void {|target3:Foo|}(); + } + }"; + + var itemForBar44 = new TestInheritanceMemberItem( + lineNumber: 4, + memberName: "Class Bar44", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "interface IBar", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementedInterface))); + + var itemForFooInMarkup1 = new TestInheritanceMemberItem( + lineNumber: 7, + memberName: "Sub Bar44.Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "void IBar.Foo()", + locationTag: "target3", + relationship: InheritanceRelationship.ImplementedMember))); + + var itemForIBar = new TestInheritanceMemberItem( + lineNumber: 4, + memberName: "interface IBar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Class Bar44", + locationTag: "target2", + relationship: InheritanceRelationship.ImplementingType))); + + var itemForFooInMarkup2 = new TestInheritanceMemberItem( + lineNumber: 6, + memberName: "void IBar.Foo()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "Sub Bar44.Foo()", + locationTag: "target4", + relationship: InheritanceRelationship.ImplementingMember))); + + return VerifyInDifferentProjectsAsync( + (markup1, LanguageNames.VisualBasic), + (markup2, LanguageNames.CSharp), + new[] { itemForBar44, itemForFooInMarkup1 }, + new[] { itemForIBar, itemForFooInMarkup2 }); + } + } +} diff --git a/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs b/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs index 9223b234e5ff7..d1e44db5c6777 100644 --- a/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs +++ b/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs @@ -45,7 +45,7 @@ internal enum InheritanceRelationship /// /// Implemented member for member in class or structure. Shown as I↑ /// - ImplmentedMember = 32, + ImplementedMember = 32, /// /// Overriden member for member in class or structure. Shown as O↑ diff --git a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs index a5278c436a8df..97e9d6a53a096 100644 --- a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs +++ b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs @@ -20,7 +20,7 @@ private static readonly ImmutableHashSet s_relationship = ImmutableHashSet.Empty .Add(InheritanceRelationship.ImplementedInterface) .Add(InheritanceRelationship.InheritedInterface) - .Add(InheritanceRelationship.ImplmentedMember); + .Add(InheritanceRelationship.ImplementedMember); private static readonly ImmutableHashSet s_relationshipsShownAs_I_DownArrow = ImmutableHashSet.Empty @@ -134,7 +134,7 @@ public static ImmutableArray CreateMenuItemsWithHe InheritanceRelationship.DerivedType => ServicesVSResources.Derived_types, InheritanceRelationship.InheritedInterface => ServicesVSResources.Inherited_interfaces, InheritanceRelationship.ImplementingType => ServicesVSResources.Implementing_types, - InheritanceRelationship.ImplmentedMember => ServicesVSResources.Implemented_members, + InheritanceRelationship.ImplementedMember => ServicesVSResources.Implemented_members, InheritanceRelationship.OverriddenMember => ServicesVSResources.Overridden_members, InheritanceRelationship.OverridingMember => ServicesVSResources.Overriding_members, InheritanceRelationship.ImplementingMember => ServicesVSResources.Implementing_members, From 29462956919eb2f2a141a1d7046c50d3934542bf Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Tue, 8 Jun 2021 23:33:55 -0700 Subject: [PATCH 099/127] Fix view model test --- .../InheritanceMarginViewModelTests.vb | 86 ++++++++++--------- 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/src/VisualStudio/Core/Test/InheritanceMargin/InheritanceMarginViewModelTests.vb b/src/VisualStudio/Core/Test/InheritanceMargin/InheritanceMarginViewModelTests.vb index d8c2d3b1bf24c..a41f117bace62 100644 --- a/src/VisualStudio/Core/Test/InheritanceMargin/InheritanceMarginViewModelTests.vb +++ b/src/VisualStudio/Core/Test/InheritanceMargin/InheritanceMarginViewModelTests.vb @@ -29,7 +29,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.InheritanceMargin Private Shared s_defaultMargin As Thickness = New Thickness(4, 1, 4, 1) - Private Shared s_indentMargin As Thickness = New Thickness(20, 1, 4, 1) + Private Shared s_indentMargin As Thickness = New Thickness(22, 1, 4, 1) Private Shared Async Function VerifyAsync(markup As String, languageName As String, expectedViewModels As Dictionary(Of Integer, InheritanceMarginViewModel)) As Task ' Add an lf before the document so that the line number starts @@ -96,6 +96,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.InheritanceMargin For i = 0 To expected.MenuItemViewModels.Length - 1 Dim expectedMenuItem = expected.MenuItemViewModels(i) Dim actualMenuItem = acutal.MenuItemViewModels(i) + VerifyMenuItem(expectedMenuItem, actualMenuItem) Next End Sub @@ -114,9 +115,9 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.InheritanceMargin Dim expectedMemberMenuItem = TryCast(expected, MemberMenuItemViewModel) Dim acutalMemberMenuItem = TryCast(actual, MemberMenuItemViewModel) - If expectedTargetMenuItem IsNot Nothing AndAlso acutalTargetMenuItem IsNot Nothing Then + If expectedMemberMenuItem IsNot Nothing AndAlso acutalMemberMenuItem IsNot Nothing Then Assert.Equal(expectedMemberMenuItem.Targets.Length, acutalMemberMenuItem.Targets.Length) - For i = 0 To expectedMemberMenuItem.Targets.Length + For i = 0 To expectedMemberMenuItem.Targets.Length - 1 VerifyMenuItem(expectedMemberMenuItem.Targets(i), acutalMemberMenuItem.Targets(i)) Next Return @@ -143,12 +144,12 @@ public class Bar : IBar { }" Dim tooltipTextForIBar = String.Format(ServicesVSResources._0_is_inherited, "interface IBar") - Dim targetForIBar = ImmutableArray.Create( - New TargetMenuItemViewModel("Bar", KnownMonikers.ClassPublic, "Bar", Nothing, s_defaultMargin)).CastArray(Of InheritanceMenuItemViewModel) + Dim targetForIBar = ImmutableArray.Create(Of InheritanceMenuItemViewModel)(New HeaderMenuItemViewModel(ServicesVSResources.Implementing_types, KnownMonikers.Implemented, ServicesVSResources.Implementing_types)). + Add(New TargetMenuItemViewModel("Bar", KnownMonikers.ClassPublic, "Bar", Nothing, s_indentMargin)) Dim tooltipTextForBar = String.Format(ServicesVSResources._0_is_inherited, "class Bar") - Dim targetForBar = ImmutableArray.Create( - New TargetMenuItemViewModel("IBar", KnownMonikers.InterfacePublic, "IBar", Nothing, s_defaultMargin)).CastArray(Of InheritanceMenuItemViewModel) + Dim targetForBar = ImmutableArray.Create(Of InheritanceMenuItemViewModel)(New HeaderMenuItemViewModel(ServicesVSResources.Implemented_interfaces, KnownMonikers.Implementing, ServicesVSResources.Implemented_interfaces)). + Add(New TargetMenuItemViewModel("IBar", KnownMonikers.InterfacePublic, "IBar", Nothing, s_indentMargin)) Return VerifyAsync(markup, LanguageNames.CSharp, New Dictionary(Of Integer, InheritanceMarginViewModel) From { {2, New InheritanceMarginViewModel( @@ -179,24 +180,24 @@ public class Bar : AbsBar }" Dim tooltipTextForAbsBar = String.Format(ServicesVSResources._0_is_inherited, "class AbsBar") - Dim targetForAbsBar = ImmutableArray.Create( - New TargetMenuItemViewModel("Bar", KnownMonikers.Class, "Bar", Nothing, s_defaultMargin)).CastArray(Of InheritanceMenuItemViewModel) + Dim targetForAbsBar = ImmutableArray.Create(Of InheritanceMenuItemViewModel)(New HeaderMenuItemViewModel(ServicesVSResources.Derived_types, KnownMonikers.Overridden, ServicesVSResources.Derived_types)). + Add(New TargetMenuItemViewModel("Bar", KnownMonikers.ClassPublic, "Bar", Nothing, s_indentMargin)) Dim tooltipTextForAbstractFoo = String.Format(ServicesVSResources._0_is_inherited, "abstract void AbsBar.Foo()") - Dim targetForAbsFoo = ImmutableArray.Create( - New TargetMenuItemViewModel("AbsBar.Foo()", KnownMonikers.MethodPublic, "AbsBar.Foo()", Nothing, s_defaultMargin)).CastArray(Of InheritanceMenuItemViewModel) + Dim targetForAbsFoo = ImmutableArray.Create(Of InheritanceMenuItemViewModel)(New HeaderMenuItemViewModel(ServicesVSResources.Overriding_members, KnownMonikers.Overridden, ServicesVSResources.Overriding_members)). + Add(New TargetMenuItemViewModel("Bar.Foo", KnownMonikers.MethodPublic, "Bar.Foo", Nothing, s_indentMargin)) Dim tooltipTextForBar = String.Format(ServicesVSResources._0_is_inherited, "class Bar") - Dim targetForBar = ImmutableArray.Create( - New TargetMenuItemViewModel("AbsBar", KnownMonikers.Class, "AbsBar", Nothing, s_defaultMargin)).CastArray(Of InheritanceMenuItemViewModel) + Dim targetForBar = ImmutableArray.Create(Of InheritanceMenuItemViewModel)(New HeaderMenuItemViewModel(ServicesVSResources.Base_Types, KnownMonikers.Overriding, ServicesVSResources.Base_Types)). + Add(New TargetMenuItemViewModel("AbsBar", KnownMonikers.ClassPublic, "AbsBar", Nothing, s_indentMargin)) Dim tooltipTextForOverrideFoo = String.Format(ServicesVSResources._0_is_inherited, "override void Bar.Foo()") - Dim targetForOverrideFoo = ImmutableArray.Create( - New TargetMenuItemViewModel("Bar.Foo()", KnownMonikers.MethodPublic, "Bar.Foo()", Nothing, s_defaultMargin)).CastArray(Of InheritanceMenuItemViewModel) + Dim targetForOverrideFoo = ImmutableArray.Create(Of InheritanceMenuItemViewModel)(New HeaderMenuItemViewModel(ServicesVSResources.Overridden_members, KnownMonikers.Overriding, ServicesVSResources.Overridden_members)). + Add(New TargetMenuItemViewModel("AbsBar.Foo", KnownMonikers.MethodPublic, "AbsBar.Foo", Nothing, s_indentMargin)) Return VerifyAsync(markup, LanguageNames.CSharp, New Dictionary(Of Integer, InheritanceMarginViewModel) From { {2, New InheritanceMarginViewModel( - KnownMonikers.Implemented, + KnownMonikers.Overridden, CreateTextBlock(tooltipTextForAbsBar), tooltipTextForAbsBar, 1, @@ -208,7 +209,7 @@ public class Bar : AbsBar 1, targetForAbsFoo)}, {7, New InheritanceMarginViewModel( - KnownMonikers.Implementing, + KnownMonikers.Overriding, CreateTextBlock(tooltipTextForBar), tooltipTextForBar, 1, @@ -229,21 +230,23 @@ public interface IBar2 : IBar1 { } public interface IBar3 : IBar2 { } " Dim tooltipTextForIBar1 = String.Format(ServicesVSResources._0_is_inherited, "interface IBar1") - Dim targetForIBar1 = ImmutableArray.Create( - New TargetMenuItemViewModel("IBar2", KnownMonikers.InterfacePublic, "IBar2", Nothing, s_defaultMargin), - New TargetMenuItemViewModel("IBar3", KnownMonikers.InterfacePublic, "IBar3", Nothing, s_defaultMargin)).CastArray(Of InheritanceMenuItemViewModel) + Dim targetForIBar1 = ImmutableArray.Create(Of InheritanceMenuItemViewModel)( + New HeaderMenuItemViewModel(ServicesVSResources.Implementing_types, KnownMonikers.Implemented, ServicesVSResources.Implementing_types), + New TargetMenuItemViewModel("IBar2", KnownMonikers.InterfacePublic, "IBar2", Nothing, s_indentMargin), + New TargetMenuItemViewModel("IBar3", KnownMonikers.InterfacePublic, "IBar3", Nothing, s_indentMargin)) Dim tooltipTextForIBar2 = String.Format(ServicesVSResources._0_is_inherited, "interface IBar2") Dim targetForIBar2 = ImmutableArray.Create(Of InheritanceMenuItemViewModel)( - New HeaderMenuItemViewModel(ServicesVSResources.Implementing_members, KnownMonikers.Implementing, ServicesVSResources.Implementing_members), + New HeaderMenuItemViewModel(ServicesVSResources.Inherited_interfaces, KnownMonikers.Implementing, ServicesVSResources.Inherited_interfaces), New TargetMenuItemViewModel("IBar1", KnownMonikers.InterfacePublic, "IBar1", Nothing, s_indentMargin), - New HeaderMenuItemViewModel(ServicesVSResources.Implemented_members, KnownMonikers.Implemented, ServicesVSResources.Implemented_members), + New HeaderMenuItemViewModel(ServicesVSResources.Implementing_types, KnownMonikers.Implemented, ServicesVSResources.Implementing_types), New TargetMenuItemViewModel("IBar3", KnownMonikers.InterfacePublic, "IBar3", Nothing, s_indentMargin)) Dim tooltipTextForIBar3 = String.Format(ServicesVSResources._0_is_inherited, "interface IBar3") - Dim targetForIBar3 = ImmutableArray.Create( - New TargetMenuItemViewModel("IBar1", KnownMonikers.InterfacePublic, "IBar1", Nothing, s_defaultMargin), - New TargetMenuItemViewModel("IBar2", KnownMonikers.InterfacePublic, "IBar2", Nothing, s_defaultMargin)).CastArray(Of InheritanceMenuItemViewModel) + Dim targetForIBar3 = ImmutableArray.Create(Of InheritanceMenuItemViewModel)( + New HeaderMenuItemViewModel(ServicesVSResources.Inherited_interfaces, KnownMonikers.Implementing, ServicesVSResources.Inherited_interfaces), + New TargetMenuItemViewModel("IBar1", KnownMonikers.InterfacePublic, "IBar1", Nothing, s_indentMargin), + New TargetMenuItemViewModel("IBar2", KnownMonikers.InterfacePublic, "IBar2", Nothing, s_indentMargin)).CastArray(Of InheritanceMenuItemViewModel) Return VerifyAsync(markup, LanguageNames.CSharp, New Dictionary(Of Integer, InheritanceMarginViewModel) From { {2, New InheritanceMarginViewModel( @@ -253,7 +256,7 @@ public interface IBar3 : IBar2 { } 1, targetForIBar1)}, {3, New InheritanceMarginViewModel( - KnownMonikers.Implemented, + KnownMonikers.Implementing, CreateTextBlock(tooltipTextForIBar2), tooltipTextForIBar2, 1, @@ -281,27 +284,32 @@ public class BarSample : IBar1 }" Dim tooltipTextForIBar1 = String.Format(ServicesVSResources._0_is_inherited, "interface IBar1") - Dim targetForIBar1 = ImmutableArray.Create( - New TargetMenuItemViewModel("BarSample", KnownMonikers.ClassPublic, "BarSample", Nothing, s_defaultMargin)).CastArray(Of InheritanceMenuItemViewModel) + Dim targetForIBar1 = ImmutableArray.Create(Of InheritanceMenuItemViewModel)( + New HeaderMenuItemViewModel(ServicesVSResources.Implementing_types, KnownMonikers.Implemented, ServicesVSResources.Implementing_types), + New TargetMenuItemViewModel("BarSample", KnownMonikers.ClassPublic, "BarSample", Nothing, s_indentMargin)) Dim tooltipTextForE1AndE2InInterface = ServicesVSResources.Multiple_members_are_inherited Dim targetForE1AndE2InInterface = ImmutableArray.Create(Of InheritanceMenuItemViewModel)( - New MemberMenuItemViewModel("event EventHandler e1", KnownMonikers.EventPublic, "event EventHandler e1", ImmutableArray.Create( - New TargetMenuItemViewModel("BarSample.e1", KnownMonikers.EventPublic, "BarSample.e1", Nothing, s_defaultMargin)).CastArray(Of InheritanceMenuItemViewModel)), - New MemberMenuItemViewModel("event EventHandler e2", KnownMonikers.EventPublic, "event EventHandler e2", ImmutableArray.Create( - New TargetMenuItemViewModel("BarSample.e2", KnownMonikers.EventPublic, "BarSample.e2", Nothing, s_defaultMargin)).CastArray(Of InheritanceMenuItemViewModel))) _ - .CastArray(Of InheritanceMenuItemViewModel) + New MemberMenuItemViewModel("event EventHandler IBar1.e1", KnownMonikers.EventPublic, "event EventHandler IBar1.e1", ImmutableArray.Create(Of InheritanceMenuItemViewModel)( + New HeaderMenuItemViewModel(ServicesVSResources.Implementing_members, KnownMonikers.Implemented, ServicesVSResources.Implementing_members), + New TargetMenuItemViewModel("BarSample.e1", KnownMonikers.EventPublic, "BarSample.e1", Nothing, s_indentMargin))), + New MemberMenuItemViewModel("event EventHandler IBar1.e2", KnownMonikers.EventPublic, "event EventHandler IBar1.e2", ImmutableArray.Create(Of InheritanceMenuItemViewModel)( + New HeaderMenuItemViewModel(ServicesVSResources.Implementing_members, KnownMonikers.Implemented, ServicesVSResources.Implementing_members), + New TargetMenuItemViewModel("BarSample.e2", KnownMonikers.EventPublic, "BarSample.e2", Nothing, s_indentMargin)))) Dim tooltipTextForBarSample = String.Format(ServicesVSResources._0_is_inherited, "class BarSample") - Dim targetForBarSample = ImmutableArray.Create( - New TargetMenuItemViewModel("IBar1", KnownMonikers.InterfacePublic, "IBar1", Nothing, s_defaultMargin)).CastArray(Of InheritanceMenuItemViewModel) + Dim targetForBarSample = ImmutableArray.Create(Of InheritanceMenuItemViewModel)( + New HeaderMenuItemViewModel(ServicesVSResources.Implemented_interfaces, KnownMonikers.Implementing, ServicesVSResources.Implemented_interfaces), + New TargetMenuItemViewModel("IBar1", KnownMonikers.InterfaceInternal, "IBar1", Nothing, s_indentMargin)) Dim tooltipTextForE1AndE2InBarSample = ServicesVSResources.Multiple_members_are_inherited Dim targetForE1AndE2InInBarSample = ImmutableArray.Create(Of InheritanceMenuItemViewModel)( - New MemberMenuItemViewModel("event EventHandler e1", KnownMonikers.EventPublic, "event EventHandler e1", ImmutableArray.Create( - New TargetMenuItemViewModel("IBar1.BarSample.e1", KnownMonikers.EventPublic, "IBar1.BarSample.e1", Nothing, s_defaultMargin)).CastArray(Of InheritanceMenuItemViewModel)), - New MemberMenuItemViewModel("event EventHandler e2", KnownMonikers.EventPublic, "event EventHandler e2", ImmutableArray.Create( - New TargetMenuItemViewModel("IBar1.BarSample.e2", KnownMonikers.EventPublic, "IBar1.BarSample.e2", Nothing, s_defaultMargin)).CastArray(Of InheritanceMenuItemViewModel))) _ + New MemberMenuItemViewModel("virtual event EventHandler BarSample.e1", KnownMonikers.EventPublic, "virtual event EventHandler BarSample.e1", ImmutableArray.Create(Of InheritanceMenuItemViewModel)( + New HeaderMenuItemViewModel(ServicesVSResources.Implemented_members, KnownMonikers.Implementing, ServicesVSResources.Implemented_members), + New TargetMenuItemViewModel("IBar1.e1", KnownMonikers.EventPublic, "IBar1.e1", Nothing, s_indentMargin)).CastArray(Of InheritanceMenuItemViewModel)), + New MemberMenuItemViewModel("virtual event EventHandler BarSample.e2", KnownMonikers.EventPublic, "virtual event EventHandler BarSample.e2", ImmutableArray.Create(Of InheritanceMenuItemViewModel)( + New HeaderMenuItemViewModel(ServicesVSResources.Implemented_members, KnownMonikers.Implementing, ServicesVSResources.Implemented_members), + New TargetMenuItemViewModel("IBar1.e2", KnownMonikers.EventPublic, "IBar1.e2", Nothing, s_indentMargin)).CastArray(Of InheritanceMenuItemViewModel))) _ .CastArray(Of InheritanceMenuItemViewModel) Return VerifyAsync(markup, LanguageNames.CSharp, New Dictionary(Of Integer, InheritanceMarginViewModel) From { From 44f2ede09f2a880303d8ffc2b85bfbc94fbde1ac Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Wed, 9 Jun 2021 00:19:23 -0700 Subject: [PATCH 100/127] Clean code --- .../InheritanceMarginHelpers.cs | 12 +----- .../MarginGlyph/InheritanceMargin.xaml | 2 +- .../MarginGlyph/InheritanceMargin.xaml.cs | 9 +++-- .../MarginGlyph/TargetMenuItemViewModel.cs | 30 ++------------- .../InheritanceMarginViewModelTests.vb | 37 +++++++++---------- 5 files changed, 29 insertions(+), 61 deletions(-) diff --git a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs index 97e9d6a53a096..60bd38a5c1e64 100644 --- a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs +++ b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs @@ -94,16 +94,6 @@ public static ImmutableArray CreateMenuItemViewMod /// /// Create the view models for the inheritance targets of multiple members - /// There are two cases: - /// 1. If all the targets have the same inheritance relationship. It would have this structure: - /// e.g. - /// MemberViewModel1 -> Target1ViewModel - /// Target2ViewModel - /// MemberViewModel2 -> Target4ViewModel - /// Target5ViewModel - /// - /// 2. If targets belongs to different inheritance group. It would be grouped. - /// e.g. /// MemberViewModel1 -> HeaderViewModel /// Target1ViewModel /// HeaderViewModel @@ -145,7 +135,7 @@ public static ImmutableArray CreateMenuItemsWithHe builder.Add(headerViewModel); foreach (var targetItem in targets) { - builder.Add(TargetMenuItemViewModel.Create(targetItem, indent: true)); + builder.Add(TargetMenuItemViewModel.Create(targetItem)); } return builder.ToImmutable(); diff --git a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/InheritanceMargin.xaml b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/InheritanceMargin.xaml index c93bf9692e9eb..800af9d3f31d2 100644 --- a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/InheritanceMargin.xaml +++ b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/InheritanceMargin.xaml @@ -115,7 +115,7 @@ - diff --git a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/InheritanceMargin.xaml.cs b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/InheritanceMargin.xaml.cs index d2003b6644329..cda4a9b5632a4 100644 --- a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/InheritanceMargin.xaml.cs +++ b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/InheritanceMargin.xaml.cs @@ -110,13 +110,16 @@ private void ContextMenu_OnOpen(object sender, RoutedEventArgs e) && inheritanceMarginViewModel.MenuItemViewModels.Any(vm => vm is TargetMenuItemViewModel)) { // We have two kinds of context menu. e.g. - // 1. [margin] -> Target1 + // 1. [margin] -> Header + // Target1 // Target2 // Target3 // - // 2. [margin] -> method Bar -> Target1 + // 2. [margin] -> method Bar -> Header + // -> Target1 // -> Target2 - // -> method Foo -> Target3 + // -> method Foo -> Header + // -> Target3 // -> Target4 // If the first level of the context menu contains a TargetMenuItemViewModel, it means here it is case 1, // user is viewing the targets menu. diff --git a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/TargetMenuItemViewModel.cs b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/TargetMenuItemViewModel.cs index d8b553509c76a..18e00ce27088a 100644 --- a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/TargetMenuItemViewModel.cs +++ b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/TargetMenuItemViewModel.cs @@ -15,45 +15,22 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.InheritanceMarg /// internal class TargetMenuItemViewModel : InheritanceMenuItemViewModel { - /// - /// The margin for the default case. - /// - private static readonly Thickness s_defaultMargin = new Thickness(4, 1, 4, 1); - - /// - /// The margin used when this target item needs to be indented when the target is shown with the header. - /// e.g. - /// 'I↓ Implemented members' - /// Method 'Bar' - /// 'I↑ Implementing members' - /// Method 'Foo' - /// It is 22 because the default left margin is 4, and we want to keep the same indentation margin same as solution explorer, which is 18. - /// - private static readonly Thickness s_indentMargin = new Thickness(22, 1, 4, 1); - /// /// DefinitionItem used for navigation. /// public DefinitionItem DefinitionItem { get; } - /// - /// Margin for the image moniker. - /// - public Thickness Margin { get; } - // Internal for testing purpose internal TargetMenuItemViewModel( string displayContent, ImageMoniker imageMoniker, string automationName, - DefinitionItem definitionItem, - Thickness margin) : base(displayContent, imageMoniker, automationName) + DefinitionItem definitionItem) : base(displayContent, imageMoniker, automationName) { DefinitionItem = definitionItem; - Margin = margin; } - public static TargetMenuItemViewModel Create(InheritanceTargetItem target, bool indent) + public static TargetMenuItemViewModel Create(InheritanceTargetItem target) { var displayContent = target.DisplayName; var imageMoniker = target.Glyph.GetImageMoniker(); @@ -61,8 +38,7 @@ public static TargetMenuItemViewModel Create(InheritanceTargetItem target, bool displayContent, imageMoniker, displayContent, - target.DefinitionItem, - indent ? s_indentMargin : s_defaultMargin); + target.DefinitionItem); } } } diff --git a/src/VisualStudio/Core/Test/InheritanceMargin/InheritanceMarginViewModelTests.vb b/src/VisualStudio/Core/Test/InheritanceMargin/InheritanceMarginViewModelTests.vb index a41f117bace62..c81f3978fcd6b 100644 --- a/src/VisualStudio/Core/Test/InheritanceMargin/InheritanceMarginViewModelTests.vb +++ b/src/VisualStudio/Core/Test/InheritanceMargin/InheritanceMarginViewModelTests.vb @@ -109,7 +109,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.InheritanceMargin Dim expectedTargetMenuItem = TryCast(expected, TargetMenuItemViewModel) Dim acutalTargetMenuItem = TryCast(actual, TargetMenuItemViewModel) If expectedTargetMenuItem IsNot Nothing AndAlso acutalTargetMenuItem IsNot Nothing Then - Assert.Equal(expectedTargetMenuItem.Margin, acutalTargetMenuItem.Margin) Return End If @@ -145,11 +144,11 @@ public class Bar : IBar }" Dim tooltipTextForIBar = String.Format(ServicesVSResources._0_is_inherited, "interface IBar") Dim targetForIBar = ImmutableArray.Create(Of InheritanceMenuItemViewModel)(New HeaderMenuItemViewModel(ServicesVSResources.Implementing_types, KnownMonikers.Implemented, ServicesVSResources.Implementing_types)). - Add(New TargetMenuItemViewModel("Bar", KnownMonikers.ClassPublic, "Bar", Nothing, s_indentMargin)) + Add(New TargetMenuItemViewModel("Bar", KnownMonikers.ClassPublic, "Bar", Nothing)) Dim tooltipTextForBar = String.Format(ServicesVSResources._0_is_inherited, "class Bar") Dim targetForBar = ImmutableArray.Create(Of InheritanceMenuItemViewModel)(New HeaderMenuItemViewModel(ServicesVSResources.Implemented_interfaces, KnownMonikers.Implementing, ServicesVSResources.Implemented_interfaces)). - Add(New TargetMenuItemViewModel("IBar", KnownMonikers.InterfacePublic, "IBar", Nothing, s_indentMargin)) + Add(New TargetMenuItemViewModel("IBar", KnownMonikers.InterfacePublic, "IBar", Nothing)) Return VerifyAsync(markup, LanguageNames.CSharp, New Dictionary(Of Integer, InheritanceMarginViewModel) From { {2, New InheritanceMarginViewModel( @@ -181,19 +180,19 @@ public class Bar : AbsBar Dim tooltipTextForAbsBar = String.Format(ServicesVSResources._0_is_inherited, "class AbsBar") Dim targetForAbsBar = ImmutableArray.Create(Of InheritanceMenuItemViewModel)(New HeaderMenuItemViewModel(ServicesVSResources.Derived_types, KnownMonikers.Overridden, ServicesVSResources.Derived_types)). - Add(New TargetMenuItemViewModel("Bar", KnownMonikers.ClassPublic, "Bar", Nothing, s_indentMargin)) + Add(New TargetMenuItemViewModel("Bar", KnownMonikers.ClassPublic, "Bar", Nothing)) Dim tooltipTextForAbstractFoo = String.Format(ServicesVSResources._0_is_inherited, "abstract void AbsBar.Foo()") Dim targetForAbsFoo = ImmutableArray.Create(Of InheritanceMenuItemViewModel)(New HeaderMenuItemViewModel(ServicesVSResources.Overriding_members, KnownMonikers.Overridden, ServicesVSResources.Overriding_members)). - Add(New TargetMenuItemViewModel("Bar.Foo", KnownMonikers.MethodPublic, "Bar.Foo", Nothing, s_indentMargin)) + Add(New TargetMenuItemViewModel("Bar.Foo", KnownMonikers.MethodPublic, "Bar.Foo", Nothing)) Dim tooltipTextForBar = String.Format(ServicesVSResources._0_is_inherited, "class Bar") Dim targetForBar = ImmutableArray.Create(Of InheritanceMenuItemViewModel)(New HeaderMenuItemViewModel(ServicesVSResources.Base_Types, KnownMonikers.Overriding, ServicesVSResources.Base_Types)). - Add(New TargetMenuItemViewModel("AbsBar", KnownMonikers.ClassPublic, "AbsBar", Nothing, s_indentMargin)) + Add(New TargetMenuItemViewModel("AbsBar", KnownMonikers.ClassPublic, "AbsBar", Nothing)) Dim tooltipTextForOverrideFoo = String.Format(ServicesVSResources._0_is_inherited, "override void Bar.Foo()") Dim targetForOverrideFoo = ImmutableArray.Create(Of InheritanceMenuItemViewModel)(New HeaderMenuItemViewModel(ServicesVSResources.Overridden_members, KnownMonikers.Overriding, ServicesVSResources.Overridden_members)). - Add(New TargetMenuItemViewModel("AbsBar.Foo", KnownMonikers.MethodPublic, "AbsBar.Foo", Nothing, s_indentMargin)) + Add(New TargetMenuItemViewModel("AbsBar.Foo", KnownMonikers.MethodPublic, "AbsBar.Foo", Nothing)) Return VerifyAsync(markup, LanguageNames.CSharp, New Dictionary(Of Integer, InheritanceMarginViewModel) From { {2, New InheritanceMarginViewModel( @@ -232,21 +231,21 @@ public interface IBar3 : IBar2 { } Dim tooltipTextForIBar1 = String.Format(ServicesVSResources._0_is_inherited, "interface IBar1") Dim targetForIBar1 = ImmutableArray.Create(Of InheritanceMenuItemViewModel)( New HeaderMenuItemViewModel(ServicesVSResources.Implementing_types, KnownMonikers.Implemented, ServicesVSResources.Implementing_types), - New TargetMenuItemViewModel("IBar2", KnownMonikers.InterfacePublic, "IBar2", Nothing, s_indentMargin), - New TargetMenuItemViewModel("IBar3", KnownMonikers.InterfacePublic, "IBar3", Nothing, s_indentMargin)) + New TargetMenuItemViewModel("IBar2", KnownMonikers.InterfacePublic, "IBar2", Nothing), + New TargetMenuItemViewModel("IBar3", KnownMonikers.InterfacePublic, "IBar3", Nothing)) Dim tooltipTextForIBar2 = String.Format(ServicesVSResources._0_is_inherited, "interface IBar2") Dim targetForIBar2 = ImmutableArray.Create(Of InheritanceMenuItemViewModel)( New HeaderMenuItemViewModel(ServicesVSResources.Inherited_interfaces, KnownMonikers.Implementing, ServicesVSResources.Inherited_interfaces), - New TargetMenuItemViewModel("IBar1", KnownMonikers.InterfacePublic, "IBar1", Nothing, s_indentMargin), + New TargetMenuItemViewModel("IBar1", KnownMonikers.InterfacePublic, "IBar1", Nothing), New HeaderMenuItemViewModel(ServicesVSResources.Implementing_types, KnownMonikers.Implemented, ServicesVSResources.Implementing_types), - New TargetMenuItemViewModel("IBar3", KnownMonikers.InterfacePublic, "IBar3", Nothing, s_indentMargin)) + New TargetMenuItemViewModel("IBar3", KnownMonikers.InterfacePublic, "IBar3", Nothing)) Dim tooltipTextForIBar3 = String.Format(ServicesVSResources._0_is_inherited, "interface IBar3") Dim targetForIBar3 = ImmutableArray.Create(Of InheritanceMenuItemViewModel)( New HeaderMenuItemViewModel(ServicesVSResources.Inherited_interfaces, KnownMonikers.Implementing, ServicesVSResources.Inherited_interfaces), - New TargetMenuItemViewModel("IBar1", KnownMonikers.InterfacePublic, "IBar1", Nothing, s_indentMargin), - New TargetMenuItemViewModel("IBar2", KnownMonikers.InterfacePublic, "IBar2", Nothing, s_indentMargin)).CastArray(Of InheritanceMenuItemViewModel) + New TargetMenuItemViewModel("IBar1", KnownMonikers.InterfacePublic, "IBar1", Nothing), + New TargetMenuItemViewModel("IBar2", KnownMonikers.InterfacePublic, "IBar2", Nothing)).CastArray(Of InheritanceMenuItemViewModel) Return VerifyAsync(markup, LanguageNames.CSharp, New Dictionary(Of Integer, InheritanceMarginViewModel) From { {2, New InheritanceMarginViewModel( @@ -286,30 +285,30 @@ public class BarSample : IBar1 Dim tooltipTextForIBar1 = String.Format(ServicesVSResources._0_is_inherited, "interface IBar1") Dim targetForIBar1 = ImmutableArray.Create(Of InheritanceMenuItemViewModel)( New HeaderMenuItemViewModel(ServicesVSResources.Implementing_types, KnownMonikers.Implemented, ServicesVSResources.Implementing_types), - New TargetMenuItemViewModel("BarSample", KnownMonikers.ClassPublic, "BarSample", Nothing, s_indentMargin)) + New TargetMenuItemViewModel("BarSample", KnownMonikers.ClassPublic, "BarSample", Nothing)) Dim tooltipTextForE1AndE2InInterface = ServicesVSResources.Multiple_members_are_inherited Dim targetForE1AndE2InInterface = ImmutableArray.Create(Of InheritanceMenuItemViewModel)( New MemberMenuItemViewModel("event EventHandler IBar1.e1", KnownMonikers.EventPublic, "event EventHandler IBar1.e1", ImmutableArray.Create(Of InheritanceMenuItemViewModel)( New HeaderMenuItemViewModel(ServicesVSResources.Implementing_members, KnownMonikers.Implemented, ServicesVSResources.Implementing_members), - New TargetMenuItemViewModel("BarSample.e1", KnownMonikers.EventPublic, "BarSample.e1", Nothing, s_indentMargin))), + New TargetMenuItemViewModel("BarSample.e1", KnownMonikers.EventPublic, "BarSample.e1", Nothing))), New MemberMenuItemViewModel("event EventHandler IBar1.e2", KnownMonikers.EventPublic, "event EventHandler IBar1.e2", ImmutableArray.Create(Of InheritanceMenuItemViewModel)( New HeaderMenuItemViewModel(ServicesVSResources.Implementing_members, KnownMonikers.Implemented, ServicesVSResources.Implementing_members), - New TargetMenuItemViewModel("BarSample.e2", KnownMonikers.EventPublic, "BarSample.e2", Nothing, s_indentMargin)))) + New TargetMenuItemViewModel("BarSample.e2", KnownMonikers.EventPublic, "BarSample.e2", Nothing)))) Dim tooltipTextForBarSample = String.Format(ServicesVSResources._0_is_inherited, "class BarSample") Dim targetForBarSample = ImmutableArray.Create(Of InheritanceMenuItemViewModel)( New HeaderMenuItemViewModel(ServicesVSResources.Implemented_interfaces, KnownMonikers.Implementing, ServicesVSResources.Implemented_interfaces), - New TargetMenuItemViewModel("IBar1", KnownMonikers.InterfaceInternal, "IBar1", Nothing, s_indentMargin)) + New TargetMenuItemViewModel("IBar1", KnownMonikers.InterfaceInternal, "IBar1", Nothing)) Dim tooltipTextForE1AndE2InBarSample = ServicesVSResources.Multiple_members_are_inherited Dim targetForE1AndE2InInBarSample = ImmutableArray.Create(Of InheritanceMenuItemViewModel)( New MemberMenuItemViewModel("virtual event EventHandler BarSample.e1", KnownMonikers.EventPublic, "virtual event EventHandler BarSample.e1", ImmutableArray.Create(Of InheritanceMenuItemViewModel)( New HeaderMenuItemViewModel(ServicesVSResources.Implemented_members, KnownMonikers.Implementing, ServicesVSResources.Implemented_members), - New TargetMenuItemViewModel("IBar1.e1", KnownMonikers.EventPublic, "IBar1.e1", Nothing, s_indentMargin)).CastArray(Of InheritanceMenuItemViewModel)), + New TargetMenuItemViewModel("IBar1.e1", KnownMonikers.EventPublic, "IBar1.e1", Nothing)).CastArray(Of InheritanceMenuItemViewModel)), New MemberMenuItemViewModel("virtual event EventHandler BarSample.e2", KnownMonikers.EventPublic, "virtual event EventHandler BarSample.e2", ImmutableArray.Create(Of InheritanceMenuItemViewModel)( New HeaderMenuItemViewModel(ServicesVSResources.Implemented_members, KnownMonikers.Implementing, ServicesVSResources.Implemented_members), - New TargetMenuItemViewModel("IBar1.e2", KnownMonikers.EventPublic, "IBar1.e2", Nothing, s_indentMargin)).CastArray(Of InheritanceMenuItemViewModel))) _ + New TargetMenuItemViewModel("IBar1.e2", KnownMonikers.EventPublic, "IBar1.e2", Nothing)).CastArray(Of InheritanceMenuItemViewModel))) _ .CastArray(Of InheritanceMenuItemViewModel) Return VerifyAsync(markup, LanguageNames.CSharp, New Dictionary(Of Integer, InheritanceMarginViewModel) From { From dd81e39c5ee33c48b31c3cc4a71602b46b43eacc Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Wed, 9 Jun 2021 10:52:08 -0700 Subject: [PATCH 101/127] Code clean up --- .../InheritanceMarginHelpers.cs | 37 ++++++++----------- .../MarginGlyph/TargetMenuItemViewModel.cs | 1 - 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs index 60bd38a5c1e64..a69dec48d131c 100644 --- a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs +++ b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs @@ -16,23 +16,23 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.InheritanceMarg { internal static class InheritanceMarginHelpers { - private static readonly ImmutableHashSet s_relationshipsShownAs_I_UpArrow + private static readonly ImmutableHashSet s_relationships_Shown_As_I_Up_Arrow = ImmutableHashSet.Empty .Add(InheritanceRelationship.ImplementedInterface) .Add(InheritanceRelationship.InheritedInterface) .Add(InheritanceRelationship.ImplementedMember); - private static readonly ImmutableHashSet s_relationshipsShownAs_I_DownArrow + private static readonly ImmutableHashSet s_relationships_Shown_As_I_Down_Arrow = ImmutableHashSet.Empty .Add(InheritanceRelationship.ImplementingType) .Add(InheritanceRelationship.ImplementingMember); - private static readonly ImmutableHashSet s_relationshipsShownAs_O_UpArrow + private static readonly ImmutableHashSet s_relationships_Shown_As_O_Up_Arrow = ImmutableHashSet.Empty .Add(InheritanceRelationship.BaseType) .Add(InheritanceRelationship.OverriddenMember); - private static readonly ImmutableHashSet s_relationshipsShownAs_O_DownArrow + private static readonly ImmutableHashSet s_relationships_Shown_As_O_Down_Arrow = ImmutableHashSet.Empty .Add(InheritanceRelationship.DerivedType) .Add(InheritanceRelationship.OverridingMember); @@ -43,35 +43,35 @@ private static readonly ImmutableHashSet s_relationship public static ImageMoniker GetMoniker(InheritanceRelationship inheritanceRelationship) { // If there are multiple targets and we have the corresponding compound image, use it - if (s_relationshipsShownAs_I_UpArrow.Any(flag => inheritanceRelationship.HasFlag(flag)) - && s_relationshipsShownAs_O_DownArrow.Any(flag => inheritanceRelationship.HasFlag(flag))) + if (s_relationships_Shown_As_I_Up_Arrow.Any(flag => inheritanceRelationship.HasFlag(flag)) + && s_relationships_Shown_As_O_Down_Arrow.Any(flag => inheritanceRelationship.HasFlag(flag))) { return KnownMonikers.ImplementingOverridden; } - if (s_relationshipsShownAs_I_UpArrow.Any(flag => inheritanceRelationship.HasFlag(flag)) - && s_relationshipsShownAs_O_UpArrow.Any(flag => inheritanceRelationship.HasFlag(flag))) + if (s_relationships_Shown_As_I_Up_Arrow.Any(flag => inheritanceRelationship.HasFlag(flag)) + && s_relationships_Shown_As_O_Up_Arrow.Any(flag => inheritanceRelationship.HasFlag(flag))) { return KnownMonikers.ImplementingOverriding; } // Otherwise, show the image based on this preference - if (s_relationshipsShownAs_I_UpArrow.Any(flag => inheritanceRelationship.HasFlag(flag))) + if (s_relationships_Shown_As_I_Up_Arrow.Any(flag => inheritanceRelationship.HasFlag(flag))) { return KnownMonikers.Implementing; } - if (s_relationshipsShownAs_I_DownArrow.Any(flag => inheritanceRelationship.HasFlag(flag))) + if (s_relationships_Shown_As_I_Down_Arrow.Any(flag => inheritanceRelationship.HasFlag(flag))) { return KnownMonikers.Implemented; } - if (s_relationshipsShownAs_O_UpArrow.Any(flag => inheritanceRelationship.HasFlag(flag))) + if (s_relationships_Shown_As_O_Up_Arrow.Any(flag => inheritanceRelationship.HasFlag(flag))) { return KnownMonikers.Overriding; } - if (s_relationshipsShownAs_O_DownArrow.Any(flag => inheritanceRelationship.HasFlag(flag))) + if (s_relationships_Shown_As_O_Down_Arrow.Any(flag => inheritanceRelationship.HasFlag(flag))) { return KnownMonikers.Overridden; } @@ -81,19 +81,14 @@ public static ImageMoniker GetMoniker(InheritanceRelationship inheritanceRelatio } public static ImmutableArray CreateMenuItemViewModelsForSingleMember(ImmutableArray targets) - { - var targetsByRelationship = targets - .OrderBy(target => target.DisplayName) + => targets.OrderBy(target => target.DisplayName) .GroupBy(target => target.RelationToMember) - .ToImmutableDictionary( - keySelector: grouping => grouping.Key, - elementSelector: grouping => grouping); - - return targetsByRelationship.SelectMany(kvp => CreateMenuItemsWithHeader(kvp.Key, kvp.Value)).ToImmutableArray(); - } + .SelectMany(grouping => CreateMenuItemsWithHeader(grouping.Key, grouping)) + .ToImmutableArray(); /// /// Create the view models for the inheritance targets of multiple members + /// e.g. /// MemberViewModel1 -> HeaderViewModel /// Target1ViewModel /// HeaderViewModel diff --git a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/TargetMenuItemViewModel.cs b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/TargetMenuItemViewModel.cs index 18e00ce27088a..a52160126b47b 100644 --- a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/TargetMenuItemViewModel.cs +++ b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/TargetMenuItemViewModel.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Windows; using Microsoft.CodeAnalysis.Editor.Wpf; using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.InheritanceMargin; From 67d13d1b8c3d4e1b938a18a837425b9f9da53c7e Mon Sep 17 00:00:00 2001 From: Youssef Victor Date: Thu, 10 Jun 2021 04:32:19 +0200 Subject: [PATCH 102/127] Add static modifier when implementing static interface member (#53939) --- .../ImplementInterfaceTests.cs | 87 +++++++++++++++++++ ...CSharpImplementInterfaceCodeFixProvider.cs | 6 +- ...actImplementInterfaceService.CodeAction.cs | 2 +- 3 files changed, 90 insertions(+), 5 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs b/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs index 433012586fa24..f7214906858db 100644 --- a/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs +++ b/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs @@ -8984,5 +8984,92 @@ public int Foo } ", parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5)); } + + [WorkItem(53925, "https://github.com/dotnet/roslyn/issues/53925")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)] + public async Task TestStaticAbstractInterfaceMember() + { + await TestInRegularAndScriptAsync(@" +interface ITest +{ + static abstract void M1(); +} + +class C : [|ITest|] +{ +} +", +@" +interface ITest +{ + static abstract void M1(); +} + +class C : ITest +{ + public static void M1() + { + throw new System.NotImplementedException(); + } +} +", parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview), index: 0, title: FeaturesResources.Implement_interface); + } + + [WorkItem(53925, "https://github.com/dotnet/roslyn/issues/53925")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)] + public async Task TestStaticAbstractInterfaceMemberExplicitly() + { + await TestInRegularAndScriptAsync(@" +interface ITest +{ + static abstract void M1(); +} + +class C : [|ITest|] +{ +} +", +@" +interface ITest +{ + static abstract void M1(); +} + +class C : ITest +{ + void ITest.M1() + { + throw new System.NotImplementedException(); + } +} +", parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview), index: 1, title: FeaturesResources.Implement_all_members_explicitly); + } + + [WorkItem(53925, "https://github.com/dotnet/roslyn/issues/53925")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)] + public async Task TestStaticAbstractInterfaceMember_ImplementAbstractly() + { + await TestInRegularAndScriptAsync(@" +interface ITest +{ + static abstract void M1(); +} + +abstract class C : [|ITest|] +{ +} +", +@" +interface ITest +{ + static abstract void M1(); +} + +abstract class C : ITest +{ + public abstract static void M1(); +} +", parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview), index: 1, title: FeaturesResources.Implement_interface_abstractly); + } } } diff --git a/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementInterfaceCodeFixProvider.cs b/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementInterfaceCodeFixProvider.cs index c38665713bb6b..89c738968cc5f 100644 --- a/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementInterfaceCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementInterfaceCodeFixProvider.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Immutable; using System.Composition; @@ -42,7 +40,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var span = context.Span; var cancellationToken = context.CancellationToken; - var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); if (!token.Span.IntersectsWith(span)) @@ -50,7 +48,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) return; } - var service = document.GetLanguageService(); + var service = document.GetRequiredLanguageService(); var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var actions = token.Parent.GetAncestorsOrThis() diff --git a/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs b/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs index 7d1ba6154ece9..3ef768df2533d 100644 --- a/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs +++ b/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs @@ -393,7 +393,7 @@ private ISymbol GenerateMember( ImplementTypePropertyGenerationBehavior propertyGenerationBehavior) { var factory = Document.GetLanguageService(); - var modifiers = new DeclarationModifiers(isAbstract: generateAbstractly, isNew: addNew, isUnsafe: addUnsafe); + var modifiers = new DeclarationModifiers(isStatic: member.IsStatic, isAbstract: generateAbstractly, isNew: addNew, isUnsafe: addUnsafe); var useExplicitInterfaceSymbol = generateInvisibly || !Service.CanImplementImplicitly; var accessibility = member.Name == memberName || generateAbstractly From d001c5da514267f43096ec3b288d3af8a7512269 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Thu, 10 Jun 2021 11:32:27 -0700 Subject: [PATCH 103/127] Adjust `static`, `abstract`, `sealed` keyword recommenders for members in interfaces. (#53928) --- .../AbstractKeywordRecommenderTests.cs | 33 ++++++++++------ .../SealedKeywordRecommenderTests.cs | 33 ++++++++++------ .../StaticKeywordRecommenderTests.cs | 38 ++++++++++++++----- .../AbstractKeywordRecommender.cs | 23 +++++++++-- .../SealedKeywordRecommender.cs | 23 +++++++++-- .../StaticKeywordRecommender.cs | 27 +++++++++++-- .../CSharp/Utilities/SyntaxKindSet.cs | 11 ++++++ 7 files changed, 147 insertions(+), 41 deletions(-) diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/AbstractKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/AbstractKeywordRecommenderTests.cs index 1687084899b56..67cd2e565fefc 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/AbstractKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/AbstractKeywordRecommenderTests.cs @@ -280,10 +280,18 @@ public async Task TestNotAfterSealed() public async Task TestNotAfterStatic() => await VerifyAbsenceAsync(@"static $$"); + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [CombinatorialData] + public async Task TestNotAfterNestedStatic([CombinatorialValues("class", "struct", "record", "record struct", "record class")] string declarationKind) + { + await VerifyAbsenceAsync(declarationKind + @" C { + static $$"); + } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedStatic() + public async Task TestAfterNestedStaticInInterface() { - await VerifyAbsenceAsync(@"class C { + await VerifyKeywordAsync(@"interface C { static $$"); } @@ -299,24 +307,27 @@ await VerifyKeywordAsync( public async Task TestNotAfterDelegate() => await VerifyAbsenceAsync(@"delegate $$"); - [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedAbstract() + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [CombinatorialData] + public async Task TestNotAfterNestedAbstract([CombinatorialValues("class", "struct", "record", "record struct", "record class", "interface")] string declarationKind) { - await VerifyAbsenceAsync(@"class C { + await VerifyAbsenceAsync(declarationKind + @" C { abstract $$"); } - [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedVirtual() + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [CombinatorialData] + public async Task TestNotAfterNestedVirtual([CombinatorialValues("class", "struct", "record", "record struct", "record class", "interface")] string declarationKind) { - await VerifyAbsenceAsync(@"class C { + await VerifyAbsenceAsync(declarationKind + @" C { virtual $$"); } - [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedSealed() + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [CombinatorialData] + public async Task TestNotAfterNestedSealed([CombinatorialValues("class", "struct", "record", "record struct", "record class", "interface")] string declarationKind) { - await VerifyAbsenceAsync(@"class C { + await VerifyAbsenceAsync(declarationKind + @" C { sealed $$"); } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/SealedKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/SealedKeywordRecommenderTests.cs index d92a732f94bb6..ec808ebd39fd3 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/SealedKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/SealedKeywordRecommenderTests.cs @@ -280,10 +280,18 @@ public async Task TestNotAfterSealed() public async Task TestNotAfterStatic() => await VerifyAbsenceAsync(@"static $$"); + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [CombinatorialData] + public async Task TestNotAfterNestedStatic([CombinatorialValues("class", "struct", "record", "record struct", "record class")] string declarationKind) + { + await VerifyAbsenceAsync(declarationKind + @" C { + static $$"); + } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedStatic() + public async Task TestAfterNestedStaticInInterface() { - await VerifyAbsenceAsync(@"class C { + await VerifyKeywordAsync(@"interface C { static $$"); } @@ -299,17 +307,19 @@ await VerifyKeywordAsync( public async Task TestNotAfterDelegate() => await VerifyAbsenceAsync(@"delegate $$"); - [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedAbstract() + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [CombinatorialData] + public async Task TestNotAfterNestedAbstract([CombinatorialValues("class", "struct", "record", "record struct", "record class", "interface")] string declarationKind) { - await VerifyAbsenceAsync(@"class C { + await VerifyAbsenceAsync(declarationKind + @" C { abstract $$"); } - [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedVirtual() + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [CombinatorialData] + public async Task TestNotAfterNestedVirtual([CombinatorialValues("class", "struct", "record", "record struct", "record class", "interface")] string declarationKind) { - await VerifyAbsenceAsync(@"class C { + await VerifyAbsenceAsync(declarationKind + @" C { virtual $$"); } @@ -321,10 +331,11 @@ await VerifyKeywordAsync( override $$"); } - [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedSealed() + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [CombinatorialData] + public async Task TestNotAfterNestedSealed([CombinatorialValues("class", "struct", "record", "record struct", "record class", "interface")] string declarationKind) { - await VerifyAbsenceAsync(@"class C { + await VerifyAbsenceAsync(declarationKind + @" C { sealed $$"); } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/StaticKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/StaticKeywordRecommenderTests.cs index 3c5b7e85e4f69..31603af3711b2 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/StaticKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/StaticKeywordRecommenderTests.cs @@ -335,31 +335,49 @@ public async Task TestNotBetweenGlobalUsings_02() //await VerifyWorkerAsync(source, absent: true, Options.Script); } - [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedAbstract() + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [CombinatorialData] + public async Task TestNotAfterNestedAbstract([CombinatorialValues("class", "struct", "record", "record struct", "record class")] string declarationKind) { - await VerifyAbsenceAsync(@"class C { + await VerifyAbsenceAsync(declarationKind + @" C { abstract $$"); } [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedVirtual() + public async Task TestAfterNestedAbstractInInterface() + { + await VerifyKeywordAsync(@"interface C { + abstract $$"); + } + + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [CombinatorialData] + public async Task TestNotAfterNestedVirtual([CombinatorialValues("class", "struct", "record", "record struct", "record class", "interface")] string declarationKind) { - await VerifyAbsenceAsync(@"class C { + await VerifyAbsenceAsync(declarationKind + @" C { virtual $$"); } - [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedOverride() + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [CombinatorialData] + public async Task TestNotAfterNestedOverride([CombinatorialValues("class", "struct", "record", "record struct", "record class", "interface")] string declarationKind) { - await VerifyAbsenceAsync(@"class C { + await VerifyAbsenceAsync(declarationKind + @" C { override $$"); } + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [CombinatorialData] + public async Task TestNotAfterNestedSealed([CombinatorialValues("class", "struct", "record", "record struct", "record class")] string declarationKind) + { + await VerifyAbsenceAsync(declarationKind + @" C { + sealed $$"); + } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedSealed() + public async Task TestAfterNestedSealedInInterface() { - await VerifyAbsenceAsync(@"class C { + await VerifyKeywordAsync(@"interface C { sealed $$"); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractKeywordRecommender.cs index a062416c33896..4c4cfb7d823b8 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractKeywordRecommender.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders { internal class AbstractKeywordRecommender : AbstractSyntacticSingleKeywordRecommender { - private static readonly ISet s_validMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) + private static readonly ISet s_validNonInterfaceMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) { SyntaxKind.ExternKeyword, SyntaxKind.InternalKeyword, @@ -24,6 +24,18 @@ internal class AbstractKeywordRecommender : AbstractSyntacticSingleKeywordRecomm SyntaxKind.OverrideKeyword, }; + private static readonly ISet s_validInterfaceMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) + { + SyntaxKind.ExternKeyword, + SyntaxKind.InternalKeyword, + SyntaxKind.NewKeyword, + SyntaxKind.PublicKeyword, + SyntaxKind.ProtectedKeyword, + SyntaxKind.StaticKeyword, + SyntaxKind.UnsafeKeyword, + SyntaxKind.OverrideKeyword, + }; + private static readonly ISet s_validTypeModifiers = new HashSet(SyntaxFacts.EqualityComparer) { SyntaxKind.InternalKeyword, @@ -44,8 +56,13 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context return context.IsGlobalStatementContext || context.IsMemberDeclarationContext( - validModifiers: s_validMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceRecordTypeDeclarations, + validModifiers: s_validNonInterfaceMemberModifiers, + validTypeDeclarations: SyntaxKindSet.ClassRecordTypeDeclarations, + canBePartial: false, + cancellationToken: cancellationToken) || + context.IsMemberDeclarationContext( + validModifiers: s_validInterfaceMemberModifiers, + validTypeDeclarations: SyntaxKindSet.InterfaceOnlyTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken) || context.IsTypeDeclarationContext( diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SealedKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SealedKeywordRecommender.cs index c0d5275af6e05..f32f0bf357d36 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SealedKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SealedKeywordRecommender.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders { internal class SealedKeywordRecommender : AbstractSyntacticSingleKeywordRecommender { - private static readonly ISet s_validMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) + private static readonly ISet s_validNonInterfaceMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) { SyntaxKind.ExternKeyword, SyntaxKind.InternalKeyword, @@ -24,6 +24,18 @@ internal class SealedKeywordRecommender : AbstractSyntacticSingleKeywordRecommen SyntaxKind.UnsafeKeyword, }; + private static readonly ISet s_validInterfaceMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) + { + SyntaxKind.ExternKeyword, + SyntaxKind.InternalKeyword, + SyntaxKind.NewKeyword, + SyntaxKind.OverrideKeyword, + SyntaxKind.PublicKeyword, + SyntaxKind.ProtectedKeyword, + SyntaxKind.StaticKeyword, + SyntaxKind.UnsafeKeyword, + }; + private static readonly ISet s_validTypeModifiers = new HashSet(SyntaxFacts.EqualityComparer) { SyntaxKind.InternalKeyword, @@ -44,8 +56,13 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context return context.IsGlobalStatementContext || context.IsMemberDeclarationContext( - validModifiers: s_validMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceRecordTypeDeclarations, + validModifiers: s_validNonInterfaceMemberModifiers, + validTypeDeclarations: SyntaxKindSet.ClassRecordTypeDeclarations, + canBePartial: false, + cancellationToken: cancellationToken) || + context.IsMemberDeclarationContext( + validModifiers: s_validInterfaceMemberModifiers, + validTypeDeclarations: SyntaxKindSet.InterfaceOnlyTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken) || context.IsTypeDeclarationContext( diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StaticKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StaticKeywordRecommender.cs index a9228a2537c46..0df962ed64606 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StaticKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StaticKeywordRecommender.cs @@ -23,7 +23,7 @@ internal class StaticKeywordRecommender : AbstractSyntacticSingleKeywordRecommen SyntaxKind.UnsafeKeyword, }; - private static readonly ISet s_validMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) + private static readonly ISet s_validNonInterfaceMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) { SyntaxKind.AsyncKeyword, SyntaxKind.ExternKeyword, @@ -37,6 +37,22 @@ internal class StaticKeywordRecommender : AbstractSyntacticSingleKeywordRecommen SyntaxKind.VolatileKeyword, }; + private static readonly ISet s_validInterfaceMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) + { + SyntaxKind.AbstractKeyword, + SyntaxKind.AsyncKeyword, + SyntaxKind.ExternKeyword, + SyntaxKind.InternalKeyword, + SyntaxKind.NewKeyword, + SyntaxKind.PublicKeyword, + SyntaxKind.PrivateKeyword, + SyntaxKind.ProtectedKeyword, + SyntaxKind.ReadOnlyKeyword, + SyntaxKind.SealedKeyword, + SyntaxKind.UnsafeKeyword, + SyntaxKind.VolatileKeyword, + }; + private static readonly ISet s_validGlobalMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) { SyntaxKind.ExternKeyword, @@ -77,8 +93,13 @@ private static bool IsValidContextForMember(CSharpSyntaxContext context, Cancell return context.SyntaxTree.IsGlobalMemberDeclarationContext(context.Position, s_validGlobalMemberModifiers, cancellationToken) || context.IsMemberDeclarationContext( - validModifiers: s_validMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, + validModifiers: s_validNonInterfaceMemberModifiers, + validTypeDeclarations: SyntaxKindSet.ClassStructRecordTypeDeclarations, + canBePartial: false, + cancellationToken: cancellationToken) || + context.IsMemberDeclarationContext( + validModifiers: s_validInterfaceMemberModifiers, + validTypeDeclarations: SyntaxKindSet.InterfaceOnlyTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Utilities/SyntaxKindSet.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Utilities/SyntaxKindSet.cs index ef4cbce90fe0c..32a6059711167 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Utilities/SyntaxKindSet.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Utilities/SyntaxKindSet.cs @@ -93,6 +93,12 @@ internal class SyntaxKindSet SyntaxKind.RecordDeclaration, }; + public static readonly ISet ClassRecordTypeDeclarations = new HashSet(SyntaxFacts.EqualityComparer) + { + SyntaxKind.ClassDeclaration, + SyntaxKind.RecordDeclaration, + }; + public static readonly ISet ClassStructRecordTypeDeclarations = new HashSet(SyntaxFacts.EqualityComparer) { SyntaxKind.ClassDeclaration, @@ -106,5 +112,10 @@ internal class SyntaxKindSet SyntaxKind.StructDeclaration, SyntaxKind.RecordStructDeclaration, }; + + public static readonly ISet InterfaceOnlyTypeDeclarations = new HashSet(SyntaxFacts.EqualityComparer) + { + SyntaxKind.InterfaceDeclaration, + }; } } From af38b77e79c2dd7a5b69414f688c32ad1d11bc0d Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Wed, 9 Jun 2021 17:10:05 -0700 Subject: [PATCH 104/127] Include ExplicitInterfaceImplementation --- .../AbstractInheritanceMarginService.cs | 4 +- .../InheritanceMarginTests.cs | 59 +++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs b/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs index cd522588ef52f..1dd5e510df5ac 100644 --- a/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs +++ b/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs @@ -92,8 +92,8 @@ private static bool CanHaveInheritanceTarget(ISymbol symbol) return false; } - if (symbol is INamedTypeSymbol or IEventSymbol or IPropertySymbol || - symbol.IsOrdinaryMethod()) + if (symbol is INamedTypeSymbol or IEventSymbol or IPropertySymbol + || symbol is IMethodSymbol { MethodKind: MethodKind.Ordinary or MethodKind.ExplicitInterfaceImplementation }) { return true; } diff --git a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs index 7f1c087caf2ba..8527060f8d614 100644 --- a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs +++ b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs @@ -989,6 +989,65 @@ public class {{|target1:Bar2|}} : IBar{lessThanToken}int{greaterThanToken}, IBar itemForFooInBar2); } + [Fact] + public Task TestCSharpExplicitInterfaceImplementation() + { + var lessThanToken = SecurityElement.Escape("<"); + var greaterThanToken = SecurityElement.Escape(">"); + var markup = $@" +interface {{|target2:IBar|}}{lessThanToken}T{greaterThanToken} +{{ + void {{|target3:Foo|}}(T t); +}} + +abstract class {{|target1:AbsBar|}} : IBar{lessThanToken}int{greaterThanToken} +{{ + void IBar{lessThanToken}int{greaterThanToken}.{{|target4:Foo|}}(int t) + {{ + throw new System.NotImplementedException(); + }} +}}"; + var itemForIBar = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "interface IBar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "class AbsBar", + locationTag: "target1", + relationship: InheritanceRelationship.Implemented))); + + var itemForFooInIBar = new TestInheritanceMemberItem( + lineNumber: 4, + memberName: "void IBar.Foo(T)", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "void AbsBar.IBar.Foo(int)", + locationTag: "target4", + relationship: InheritanceRelationship.Implemented))); + + var itemForAbsBar = new TestInheritanceMemberItem( + lineNumber: 7, + memberName: "class AbsBar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "interface IBar", + locationTag: "target2", + relationship: InheritanceRelationship.Implementing))); + + var itemForFooInAbsBar = new TestInheritanceMemberItem( + lineNumber: 9, + memberName: "void AbsBar.IBar.Foo(int)", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "void IBar.Foo(T)", + locationTag: "target3", + relationship: InheritanceRelationship.Implementing))); + + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.CSharp, + itemForIBar, + itemForFooInIBar, + itemForAbsBar, + itemForFooInAbsBar); + } + #endregion #region TestsForVisualBasic From bdd3d329a36a17be111b715f3ec14acd4d242a82 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Wed, 9 Jun 2021 17:15:07 -0700 Subject: [PATCH 105/127] Fix formatting --- .../Core/InheritanceMargin/AbstractInheritanceMarginService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs b/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs index 1dd5e510df5ac..b48d1535a71f8 100644 --- a/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs +++ b/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs @@ -93,7 +93,7 @@ private static bool CanHaveInheritanceTarget(ISymbol symbol) } if (symbol is INamedTypeSymbol or IEventSymbol or IPropertySymbol - || symbol is IMethodSymbol { MethodKind: MethodKind.Ordinary or MethodKind.ExplicitInterfaceImplementation }) + or IMethodSymbol { MethodKind: MethodKind.Ordinary or MethodKind.ExplicitInterfaceImplementation }) { return true; } From a38ded1d671316d0cbdd8a0aa6e9d0cf7ae3d34d Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Wed, 9 Jun 2021 23:28:24 -0700 Subject: [PATCH 106/127] Put escapse logic into helpers --- .../InheritanceMarginTests.cs | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs index 8527060f8d614..cb369cf1509f3 100644 --- a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs +++ b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs @@ -22,6 +22,8 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.InheritanceMargin public class InheritanceMarginTests { private const string SearchAreaTag = "SeachTag"; + private static readonly string s_lessThanToken = SecurityElement.Escape("<"); + private static readonly string s_greaterThanToken = SecurityElement.Escape(">"); #region Helpers @@ -33,6 +35,9 @@ private static Task VerifyInSingleDocumentAsync( string languageName, params TestInheritanceMemberItem[] memberItems) { + // Escapse < and > for xml + markup = markup.Replace("<", s_lessThanToken).Replace(">", s_greaterThanToken); + var workspaceFile = $@" @@ -138,12 +143,12 @@ private static async Task VerifyInDifferentProjectsAsync( Assembly2 - {markup1.markupInProject1} + {markup1.markupInProject1.Replace("<", s_lessThanToken).Replace(">", s_greaterThanToken)} - {markup2.markupInProject2} + {markup2.markupInProject2.Replace("<", s_lessThanToken).Replace(">", s_greaterThanToken)} "; @@ -933,18 +938,16 @@ public class {|target5:Bar2|} : Bar1, IBar [Fact] public Task TestCSharpFindGenericsBaseType() { - var lessThanToken = SecurityElement.Escape("<"); - var greaterThanToken = SecurityElement.Escape(">"); - var markup = $@" -public interface {{|target2:IBar|}}{lessThanToken}T{greaterThanToken} -{{ - void {{|target4:Foo|}}(); -}} + var markup = @" +public interface {|target2:IBar|} +{ + void {|target4:Foo|}(); +} -public class {{|target1:Bar2|}} : IBar{lessThanToken}int{greaterThanToken}, IBar{lessThanToken}string{greaterThanToken} -{{ - public void {{|target3:Foo|}}(); -}}"; +public class {|target1:Bar2|} : IBar, IBar +{ + public void {|target3:Foo|}(); +}"; var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, @@ -992,21 +995,19 @@ public class {{|target1:Bar2|}} : IBar{lessThanToken}int{greaterThanToken}, IBar [Fact] public Task TestCSharpExplicitInterfaceImplementation() { - var lessThanToken = SecurityElement.Escape("<"); - var greaterThanToken = SecurityElement.Escape(">"); - var markup = $@" -interface {{|target2:IBar|}}{lessThanToken}T{greaterThanToken} -{{ - void {{|target3:Foo|}}(T t); -}} + var markup = @" +interface {|target2:IBar|} +{ + void {|target3:Foo|}(T t); +} -abstract class {{|target1:AbsBar|}} : IBar{lessThanToken}int{greaterThanToken} -{{ - void IBar{lessThanToken}int{greaterThanToken}.{{|target4:Foo|}}(int t) - {{ +abstract class {|target1:AbsBar|} : IBar +{ + void IBar.{|target4:Foo|}(int t) + { throw new System.NotImplementedException(); - }} -}}"; + } +}"; var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface IBar", From 2fcedd91b8da9324b63f3e818c5d24560b725404 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Thu, 10 Jun 2021 00:39:43 -0700 Subject: [PATCH 107/127] Use CDATA --- .../InheritanceMargin/InheritanceMarginTests.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs index cb369cf1509f3..5dd0a86442d4c 100644 --- a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs +++ b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs @@ -4,7 +4,6 @@ using System.Collections.Immutable; using System.Linq; -using System.Security; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; @@ -22,8 +21,6 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.InheritanceMargin public class InheritanceMarginTests { private const string SearchAreaTag = "SeachTag"; - private static readonly string s_lessThanToken = SecurityElement.Escape("<"); - private static readonly string s_greaterThanToken = SecurityElement.Escape(">"); #region Helpers @@ -35,8 +32,8 @@ private static Task VerifyInSingleDocumentAsync( string languageName, params TestInheritanceMemberItem[] memberItems) { - // Escapse < and > for xml - markup = markup.Replace("<", s_lessThanToken).Replace(">", s_greaterThanToken); + markup = @$""; var workspaceFile = $@" @@ -143,12 +140,14 @@ private static async Task VerifyInDifferentProjectsAsync( Assembly2 - {markup1.markupInProject1.Replace("<", s_lessThanToken).Replace(">", s_greaterThanToken)} + - {markup2.markupInProject2.Replace("<", s_lessThanToken).Replace(">", s_greaterThanToken)} + "; From 76672b54127027d6fe8bb50db720c61280d6704f Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Thu, 10 Jun 2021 15:10:31 -0700 Subject: [PATCH 108/127] Add the fix in InheritanceMargin code --- .../AbstractInheritanceMarginService.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs b/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs index b48d1535a71f8..451623ab38c76 100644 --- a/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs +++ b/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs @@ -87,13 +87,16 @@ public async ValueTask> GetInheritanceMemb private static bool CanHaveInheritanceTarget(ISymbol symbol) { - if (symbol.IsStatic) + if (symbol is INamedTypeSymbol) { - return false; + return !symbol.IsStatic; } - if (symbol is INamedTypeSymbol or IEventSymbol or IPropertySymbol - or IMethodSymbol { MethodKind: MethodKind.Ordinary or MethodKind.ExplicitInterfaceImplementation }) + if (symbol is IEventSymbol or IPropertySymbol + or IMethodSymbol + { + MethodKind: MethodKind.Ordinary or MethodKind.ExplicitInterfaceImplementation or MethodKind.UserDefinedOperator + }) { return true; } From ea7291b59927a1cd577defa5208e7a8f2333f312 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Thu, 10 Jun 2021 16:19:21 -0700 Subject: [PATCH 109/127] Add a test --- .../InheritanceMarginTests.cs | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs index 5dd0a86442d4c..b35c25c5342ae 100644 --- a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs +++ b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs @@ -1048,6 +1048,75 @@ abstract class {|target1:AbsBar|} : IBar itemForFooInAbsBar); } + [Fact] + public Task TestStaticAbstractMemberInterface() + { + var markup = @" +interface {|target:I1|} where T : I1 +{ + static abstract void M1(); + static abstract int P1 { get; set; } + static abstract event EventHandler e1; + static abstract int operator +(T i1); +} + +public class {|target1:Class1|} : I1 +{ + public static int {|target3:P1|} { get => 1; set { } } + public static event EventHandler {|target4:e1|}; + public static void {|target2:M1|}() {} + public static int operator +(C1 i) => 1; +}"; + var itemForI1 = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "interface I1", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "class Class1", + locationTag: "target1", + relationship: InheritanceRelationship.Implemented))); + + var itemForM1InI1 = new TestInheritanceMemberItem( + lineNumber: 4, + memberName: "void I1.M1()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "void Class1.M1()", + locationTag: "target2", + relationship: InheritanceRelationship.Implemented))); + + var itemForP1InI1 = new TestInheritanceMemberItem( + lineNumber: 5, + memberName: "int I1.P1 { get; set; }", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "void I1.P1 { get; set; }", + locationTag: "target3", + relationship: InheritanceRelationship.Implemented))); + + var itemForE1InI1 = new TestInheritanceMemberItem( + lineNumber: 6, + memberName: "event EventHandler I1.e1", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "event EventHandler Class1.e1", + locationTag: "target4", + relationship: InheritanceRelationship.Implemented))); + + var itemForAbsClass1 = new TestInheritanceMemberItem( + lineNumber: 10, + memberName: "class Class1", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "interface I1", + locationTag: "target", + relationship: InheritanceRelationship.Implementing))); + + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.CSharp, + itemForI1, + itemForAbsClass1, + itemForM1InI1, + itemForP1InI1, + itemForE1InI1); + } + #endregion #region TestsForVisualBasic From 1193c1db800c1805035b2955865c410aa135ccde Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Thu, 10 Jun 2021 22:11:10 -0700 Subject: [PATCH 110/127] Fix static operator for find base/find implementation --- .../CSharpInheritanceMarginService.cs | 4 +++- .../InheritanceMarginTests.cs | 18 ++++++++++++++++++ .../Shared/Extensions/ITypeSymbolExtensions.cs | 1 - .../Core/Extensions/ISymbolExtensions.cs | 3 ++- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/CSharp/InheritanceMargin/CSharpInheritanceMarginService.cs b/src/EditorFeatures/CSharp/InheritanceMargin/CSharpInheritanceMarginService.cs index 02518184e9a8c..85897b9e0c8b4 100644 --- a/src/EditorFeatures/CSharp/InheritanceMargin/CSharpInheritanceMarginService.cs +++ b/src/EditorFeatures/CSharp/InheritanceMargin/CSharpInheritanceMarginService.cs @@ -43,7 +43,8 @@ protected override ImmutableArray GetMembers(IEnumerable SyntaxKind.MethodDeclaration, SyntaxKind.PropertyDeclaration, SyntaxKind.EventDeclaration, - SyntaxKind.IndexerDeclaration)) + SyntaxKind.IndexerDeclaration, + SyntaxKind.OperatorDeclaration)) { builder.Add(member); } @@ -69,6 +70,7 @@ protected override SyntaxToken GetDeclarationToken(SyntaxNode declarationNode) VariableDeclaratorSyntax variableDeclaratorNode => variableDeclaratorNode.Identifier, TypeDeclarationSyntax baseTypeDeclarationNode => baseTypeDeclarationNode.Identifier, IndexerDeclarationSyntax indexerDeclarationNode => indexerDeclarationNode.ThisKeyword, + OperatorDeclarationSyntax operatorDeclarationNode => operatorDeclarationNode.OperatorToken, // Shouldn't reach here since the input declaration nodes are coming from GetMembers() method above _ => throw ExceptionUtilities.UnexpectedValue(declarationNode), }; diff --git a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs index b35c25c5342ae..25f8c425ec7c2 100644 --- a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs +++ b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs @@ -1048,6 +1048,24 @@ abstract class {|target1:AbsBar|} : IBar itemForFooInAbsBar); } +// [Fact] +// public Task Test() +// { +// var markup = @" +//interface {|target:I1|} where T : I1 +//{ +// static abstract int operator +(T i1); +//} + +//public class {|target1:Class1|} : I1 +//{ +// public static int operator +(Class1 i) => 1; +//}"; +// return VerifyInSingleDocumentAsync( +// markup, +// LanguageNames.CSharp); +// } + [Fact] public Task TestStaticAbstractMemberInterface() { diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/ITypeSymbolExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/ITypeSymbolExtensions.cs index 49c76de2e08fb..cd195f4789a1e 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/ITypeSymbolExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/ITypeSymbolExtensions.cs @@ -170,7 +170,6 @@ where SymbolEquivalenceComparer.Instance.Equals(explicitInterfaceMethod, constru from baseType in typeSymbol.GetBaseTypesAndThis() from member in baseType.GetMembers(constructedInterfaceMember.Name).OfType() where member.DeclaredAccessibility == Accessibility.Public && - !member.IsStatic && SignatureComparer.Instance.HaveSameSignatureAndConstraintsAndReturnTypeAndAccessors(member, constructedInterfaceMember, syntaxFacts.IsCaseSensitive) select member; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs index db8f0b67019e5..33ae3b59dbf05 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs @@ -137,7 +137,8 @@ public static bool IsImplementableMember([NotNullWhen(returnValue: true)] this I var methodSymbol = (IMethodSymbol)symbol; if (methodSymbol.MethodKind == MethodKind.Ordinary || methodSymbol.MethodKind == MethodKind.PropertyGet || - methodSymbol.MethodKind == MethodKind.PropertySet) + methodSymbol.MethodKind == MethodKind.PropertySet || + methodSymbol.MethodKind == MethodKind.UserDefinedOperator) { return true; } From cc5b7c129fdac6a3c12aa274c8bcb65fafb62a03 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Fri, 11 Jun 2021 07:04:08 -0700 Subject: [PATCH 111/127] Enable completion of implicit/explicit keywords in interfaces (#54011) --- .../ExplicitKeywordRecommenderTests.cs | 153 +++++++++++++++++- .../ImplicitKeywordRecommenderTests.cs | 153 +++++++++++++++++- .../ExplicitKeywordRecommender.cs | 21 ++- .../ImplicitKeywordRecommender.cs | 21 ++- 4 files changed, 328 insertions(+), 20 deletions(-) diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ExplicitKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ExplicitKeywordRecommenderTests.cs index b065e3d8da967..68389be670a4a 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ExplicitKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ExplicitKeywordRecommenderTests.cs @@ -238,25 +238,113 @@ public async Task TestNotAfterStaticPublic() => await VerifyAbsenceAsync(@"static public $$"); [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestAfterNestedStaticPublic() + public async Task TestAfterNestedStaticPublicInClass() { await VerifyKeywordAsync( @"class C { static public $$"); } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedStaticPublicInInterface() + { + await VerifyAbsenceAsync( +@"interface C { + static public $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedAbstractPublicInInterface() + { + await VerifyAbsenceAsync( +@"interface C { + abstract public $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterNestedStaticAbstractPublicInInterface() + { + await VerifyKeywordAsync( +@"interface C { + static abstract public $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterNestedAbstractStaticPublicInInterface() + { + await VerifyKeywordAsync( +@"interface C { + abstract static public $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterNestedStaticAbstractInInterface() + { + await VerifyKeywordAsync( +@"interface C { + static abstract $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterNestedAbstractStaticInInterface() + { + await VerifyKeywordAsync( +@"interface C { + abstract static $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedStaticInInterface() + { + await VerifyAbsenceAsync( +@"interface C { + static $$"); + } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestNotAfterPublicStatic() => await VerifyAbsenceAsync(@"public static $$"); [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestAfterNestedPublicStatic() + public async Task TestAfterNestedPublicStaticInClass() { await VerifyKeywordAsync( @"class C { public static $$"); } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedPublicStaticInInterface() + { + await VerifyAbsenceAsync( +@"interface C { + public static $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedPublicAbstractInInterface() + { + await VerifyAbsenceAsync( +@"interface C { + public abstract $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterNestedPublicStaticAbstractInInterface() + { + await VerifyKeywordAsync( +@"interface C { + public static abstract $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterNestedPublicAbstractStaticInInterface() + { + await VerifyKeywordAsync( +@"interface C { + public abstract static $$"); + } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestNotAfterInvalidPublic() => await VerifyAbsenceAsync(@"virtual public $$"); @@ -313,35 +401,35 @@ await VerifyAbsenceAsync( } [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedAbstract() + public async Task TestNotAfterNestedAbstractInClass() { await VerifyAbsenceAsync(@"class C { abstract $$"); } [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedVirtual() + public async Task TestNotAfterNestedVirtualInClass() { await VerifyAbsenceAsync(@"class C { virtual $$"); } [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedOverride() + public async Task TestNotAfterNestedOverrideInClass() { await VerifyAbsenceAsync(@"class C { override $$"); } [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestAfterNestedSealed() + public async Task TestNotAfterNestedSealedInClass() { await VerifyAbsenceAsync(@"class C { sealed $$"); } [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedReadOnly() + public async Task TestNotAfterNestedReadOnlyInClass() { await VerifyAbsenceAsync(@"class C { readonly $$"); @@ -349,10 +437,59 @@ await VerifyAbsenceAsync(@"class C { [WorkItem(544102, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544102")] [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestAfterUnsafeStaticPublic() + public async Task TestAfterNestedUnsafeStaticPublicInClass() { await VerifyKeywordAsync(@"class C { unsafe static public $$"); } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedAbstractInInterface() + { + await VerifyAbsenceAsync(@"interface C { + abstract $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedVirtualInInterface() + { + await VerifyAbsenceAsync(@"interface C { + virtual $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedOverrideInInterface() + { + await VerifyAbsenceAsync(@"interface C { + override $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedSealedInInterface() + { + await VerifyAbsenceAsync(@"interface C { + sealed $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedReadOnlyInInterface() + { + await VerifyAbsenceAsync(@"interface C { + readonly $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterUnsafeStaticAbstractInInterface() + { + await VerifyKeywordAsync(@"interface C { + unsafe static abstract $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterExternStaticAbstractInInterface() + { + await VerifyAbsenceAsync(@"interface C { + extern static abstract $$"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ImplicitKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ImplicitKeywordRecommenderTests.cs index dea0b522ee7f3..5967a4a667651 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ImplicitKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ImplicitKeywordRecommenderTests.cs @@ -238,25 +238,113 @@ public async Task TestNotAfterStaticPublic() => await VerifyAbsenceAsync(@"static public $$"); [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestAfterNestedStaticPublic() + public async Task TestAfterNestedStaticPublicInClass() { await VerifyKeywordAsync( @"class C { static public $$"); } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedStaticPublicInInterface() + { + await VerifyAbsenceAsync( +@"interface C { + static public $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedAbstractPublicInInterface() + { + await VerifyAbsenceAsync( +@"interface C { + abstract public $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterNestedStaticAbstractPublicInInterface() + { + await VerifyKeywordAsync( +@"interface C { + static abstract public $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterNestedAbstractStaticPublicInInterface() + { + await VerifyKeywordAsync( +@"interface C { + abstract static public $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterNestedStaticAbstractInInterface() + { + await VerifyKeywordAsync( +@"interface C { + static abstract $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterNestedAbstractStaticInInterface() + { + await VerifyKeywordAsync( +@"interface C { + abstract static $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedStaticInInterface() + { + await VerifyAbsenceAsync( +@"interface C { + static $$"); + } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestNotAfterPublicStatic() => await VerifyAbsenceAsync(@"public static $$"); [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestAfterNestedPublicStatic() + public async Task TestAfterNestedPublicStaticInClass() { await VerifyKeywordAsync( @"class C { public static $$"); } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedPublicStaticInInterface() + { + await VerifyAbsenceAsync( +@"interface C { + public static $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedPublicAbstractInInterface() + { + await VerifyAbsenceAsync( +@"interface C { + public abstract $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterNestedPublicStaticAbstractInInterface() + { + await VerifyKeywordAsync( +@"interface C { + public static abstract $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterNestedPublicAbstractStaticInInterface() + { + await VerifyKeywordAsync( +@"interface C { + public abstract static $$"); + } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestNotAfterInvalidPublic() => await VerifyAbsenceAsync(@"virtual public $$"); @@ -313,35 +401,35 @@ await VerifyAbsenceAsync( } [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedAbstract() + public async Task TestNotAfterNestedAbstractInClass() { await VerifyAbsenceAsync(@"class C { abstract $$"); } [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedVirtual() + public async Task TestNotAfterNestedVirtualInClass() { await VerifyAbsenceAsync(@"class C { virtual $$"); } [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedOverride() + public async Task TestNotAfterNestedOverrideInClass() { await VerifyAbsenceAsync(@"class C { override $$"); } [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestAfterNestedSealed() + public async Task TestNotAfterNestedSealedInClass() { await VerifyAbsenceAsync(@"class C { sealed $$"); } [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedReadOnly() + public async Task TestNotAfterNestedReadOnlyInClass() { await VerifyAbsenceAsync(@"class C { readonly $$"); @@ -349,10 +437,59 @@ await VerifyAbsenceAsync(@"class C { [WorkItem(544103, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544103")] [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestAfterUnsafeStaticPublic() + public async Task TestAfterNestedUnsafeStaticPublicInClass() { await VerifyKeywordAsync(@"class C { unsafe static public $$"); } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedAbstractInInterface() + { + await VerifyAbsenceAsync(@"interface C { + abstract $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedVirtualInInterface() + { + await VerifyAbsenceAsync(@"interface C { + virtual $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedOverrideInInterface() + { + await VerifyAbsenceAsync(@"interface C { + override $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedSealedInInterface() + { + await VerifyAbsenceAsync(@"interface C { + sealed $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterNestedReadOnlyInInterface() + { + await VerifyAbsenceAsync(@"interface C { + readonly $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterUnsafeStaticAbstractInInterface() + { + await VerifyKeywordAsync(@"interface C { + unsafe static abstract $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterExternStaticAbstractInInterface() + { + await VerifyAbsenceAsync(@"interface C { + extern static abstract $$"); + } } } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ExplicitKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ExplicitKeywordRecommender.cs index c6ed3cd79e1f0..b7198e35ba18d 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ExplicitKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ExplicitKeywordRecommender.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders { internal class ExplicitKeywordRecommender : AbstractSyntacticSingleKeywordRecommender { - private static readonly ISet s_validMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) + private static readonly ISet s_validNonInterfaceMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) { SyntaxKind.StaticKeyword, SyntaxKind.PublicKeyword, @@ -21,6 +21,14 @@ internal class ExplicitKeywordRecommender : AbstractSyntacticSingleKeywordRecomm SyntaxKind.UnsafeKeyword, }; + private static readonly ISet s_validInterfaceMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) + { + SyntaxKind.StaticKeyword, + SyntaxKind.PublicKeyword, + SyntaxKind.AbstractKeyword, + SyntaxKind.UnsafeKeyword, + }; + public ExplicitKeywordRecommender() : base(SyntaxKind.ExplicitKeyword) { @@ -28,7 +36,7 @@ public ExplicitKeywordRecommender() protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { - if (context.IsMemberDeclarationContext(validModifiers: s_validMemberModifiers, validTypeDeclarations: SyntaxKindSet.ClassStructRecordTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) + if (context.IsMemberDeclarationContext(validModifiers: s_validNonInterfaceMemberModifiers, validTypeDeclarations: SyntaxKindSet.ClassStructRecordTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) { // operators must be both public and static var modifiers = context.PrecedingModifiers; @@ -37,6 +45,15 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context modifiers.Contains(SyntaxKind.PublicKeyword) && modifiers.Contains(SyntaxKind.StaticKeyword); } + else if (context.IsMemberDeclarationContext(validModifiers: s_validInterfaceMemberModifiers, validTypeDeclarations: SyntaxKindSet.InterfaceOnlyTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) + { + // operators must be both abstract and static + var modifiers = context.PrecedingModifiers; + + return + modifiers.Contains(SyntaxKind.AbstractKeyword) && + modifiers.Contains(SyntaxKind.StaticKeyword); + } return false; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ImplicitKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ImplicitKeywordRecommender.cs index 400b2107e28e3..5ba148272eb74 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ImplicitKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ImplicitKeywordRecommender.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders { internal class ImplicitKeywordRecommender : AbstractSyntacticSingleKeywordRecommender { - private static readonly ISet s_validMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) + private static readonly ISet s_validNonInterfaceMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) { SyntaxKind.StaticKeyword, SyntaxKind.PublicKeyword, @@ -21,6 +21,14 @@ internal class ImplicitKeywordRecommender : AbstractSyntacticSingleKeywordRecomm SyntaxKind.UnsafeKeyword, }; + private static readonly ISet s_validInterfaceMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) + { + SyntaxKind.StaticKeyword, + SyntaxKind.PublicKeyword, + SyntaxKind.AbstractKeyword, + SyntaxKind.UnsafeKeyword, + }; + public ImplicitKeywordRecommender() : base(SyntaxKind.ImplicitKeyword) { @@ -28,7 +36,7 @@ public ImplicitKeywordRecommender() protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { - if (context.IsMemberDeclarationContext(validModifiers: s_validMemberModifiers, validTypeDeclarations: SyntaxKindSet.ClassStructRecordTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) + if (context.IsMemberDeclarationContext(validModifiers: s_validNonInterfaceMemberModifiers, validTypeDeclarations: SyntaxKindSet.ClassStructRecordTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) { // operators must be both public and static var modifiers = context.PrecedingModifiers; @@ -37,6 +45,15 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context modifiers.Contains(SyntaxKind.PublicKeyword) && modifiers.Contains(SyntaxKind.StaticKeyword); } + else if (context.IsMemberDeclarationContext(validModifiers: s_validInterfaceMemberModifiers, validTypeDeclarations: SyntaxKindSet.InterfaceOnlyTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) + { + // operators must be both abstract and static + var modifiers = context.PrecedingModifiers; + + return + modifiers.Contains(SyntaxKind.AbstractKeyword) && + modifiers.Contains(SyntaxKind.StaticKeyword); + } return false; } From 40622e3a7e8f1699100c001e1543742d6b560c77 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Fri, 11 Jun 2021 15:57:28 -0700 Subject: [PATCH 112/127] Fix FAR for method --- ...ndReferencesTests.OrdinaryMethodSymbols.vb | 120 ++++++++++++++++++ ...dOrPropertyOrEventSymbolReferenceFinder.cs | 4 - .../FindSymbols/SymbolFinder_Hierarchy.cs | 4 +- 3 files changed, 122 insertions(+), 6 deletions(-) diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OrdinaryMethodSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OrdinaryMethodSymbols.vb index 159df9d86c166..b56ee8a7847a3 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OrdinaryMethodSymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OrdinaryMethodSymbols.vb @@ -3786,5 +3786,125 @@ End Class Await TestStreamingFeature(input, host) End Function + + + Public Async Function TestMemberStaticAbstractMethodFromInterface(kind As TestKind, host As TestHost) As Task + Dim input = + + + + interface I1 + { + static abstract void {|Definition:M$$1|}(); + } + class C1_1 : I1 + { + public static void {|Definition:M1|}() { } + } + class C1_2 : I1 + { + static void I1.{|Definition:M1|}() { } + } + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + Public Async Function TestDerivedMemberStaticAbstractMethodViaFeature1(host As TestHost) As Task + Dim input = + + + + interface I1 + { + static abstract void {|Definition:M1|}(); + } + class C1_1 : I1 + { + public static void {|Definition:M$$1|}() { } + } + class C1_2 : I1 + { + static void I1.M1() { } + } + + + + Await TestStreamingFeature(input, host) + End Function + + + Public Async Function TestDerivedMemberStaticAbstractMethodViaFeature2(host As TestHost) As Task + Dim input = + + + + interface I1 + { + static abstract void {|Definition:M1|}(); + } + class C1_1 : I1 + { + public static void M1() { } + } + class C1_2 : I1 + { + static void I1.{|Definition:M$$1|}() { } + } + + + + Await TestStreamingFeature(input, host) + End Function + + + Public Async Function TestDerivedMemberStaticAbstractMethodViaAPI1(host As TestHost) As Task + Dim input = + + + + interface I1 + { + static abstract void {|Definition:M1|}(); + } + class C1_1 : I1 + { + public static void {|Definition:M$$1|}() { } + } + class C1_2 : I1 + { + static void I1.{|Definition:M1|}() { } + } + + + + Await TestAPI(input, host) + End Function + + + Public Async Function TestDerivedMemberStaticAbstractMethodViaAPI2(host As TestHost) As Task + Dim input = + + + + interface I1 + { + static abstract void {|Definition:M1|}(); + } + class C1_1 : I1 + { + public static void {|Definition:M1|}() { } + } + class C1_2 : I1 + { + static void I1.{|Definition:M$$1|}() { } + } + + + + Await TestAPI(input, host) + End Function End Class End Namespace diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractMethodOrPropertyOrEventSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractMethodOrPropertyOrEventSymbolReferenceFinder.cs index 4b2948900236d..29e40ca4a9817 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractMethodOrPropertyOrEventSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractMethodOrPropertyOrEventSymbolReferenceFinder.cs @@ -26,10 +26,6 @@ protected AbstractMethodOrPropertyOrEventSymbolReferenceFinder() FindReferencesCascadeDirection cascadeDirection, CancellationToken cancellationToken) { - // Static methods can't cascade. - if (symbol.IsStatic) - return ImmutableArray<(ISymbol symbol, FindReferencesCascadeDirection cascadeDirection)>.Empty; - if (symbol.IsImplementableMember()) { // We have an interface method. Walk down the inheritance hierarchy and find all implementations of diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs index c61be915c53c7..e9e2640818a1e 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs @@ -94,7 +94,7 @@ internal static async Task> FindImplementedInterfaceMemb ISymbol symbol, Solution solution, IImmutableSet projects = null, CancellationToken cancellationToken = default) { // Member can only implement interface members if it is an explicit member, or if it is - // public and non static. + // public if (symbol != null) { var explicitImplementations = symbol.ExplicitInterfaceImplementations(); @@ -103,7 +103,7 @@ internal static async Task> FindImplementedInterfaceMemb return explicitImplementations; } else if ( - symbol.DeclaredAccessibility == Accessibility.Public && !symbol.IsStatic && + symbol.DeclaredAccessibility == Accessibility.Public && (symbol.ContainingType.TypeKind == TypeKind.Class || symbol.ContainingType.TypeKind == TypeKind.Struct)) { // Interface implementation is a tricky thing. A method may implement an interface From 551c8ad7257f42427f5e476cdb3e7943c7dcce42 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Fri, 11 Jun 2021 16:38:07 -0700 Subject: [PATCH 113/127] Add event tests and property tests --- .../FindReferencesTests.EventSymbols.vb | 145 ++++++++++++++++++ .../FindReferencesTests.PropertySymbols.vb | 130 ++++++++++++++++ 2 files changed, 275 insertions(+) diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.EventSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.EventSymbols.vb index 00372e514edbd..520dbba6235ca 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.EventSymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.EventSymbols.vb @@ -316,5 +316,150 @@ End Interface Await TestAPIAndFeature(input, kind, host) End Function + + + Public Async Function TestStaticAbstractEventInInterface(kind As TestKind, host As TestHost) As Task + Dim input = + + + +interface I3 +{ + abstract static event System.Action {|Definition:E$$3|}; +} + +class C3_1 : I3 +{ + public static event System.Action {|Definition:E3|}; +} + +class C3_2 : I3 +{ + static event System.Action I3.{|Definition:E3|} + { + add { } + remove { } + } +} + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + Public Async Function TestStaticAbstractEventViaFeature1(host As TestHost) As Task + Dim input = + + + +interface I3 +{ + abstract static event System.Action {|Definition:E3|}; +} + +class C3_1 : I3 +{ + public static event System.Action {|Definition:E$$3|}; +} + +class C3_2 : I3 +{ + static event System.Action I3.E3 + { + add { } + remove { } + } +} + + + Await TestStreamingFeature(input, host) + End Function + + + Public Async Function TestStaticAbstractEventViaFeature2(host As TestHost) As Task + Dim input = + + + +interface I3 +{ + abstract static event System.Action {|Definition:E3|}; +} + +class C3_1 : I3 +{ + public static event System.Action E3; +} + +class C3_2 : I3 +{ + static event System.Action I3.{|Definition:E$$3|} + { + add { } + remove { } + } +} + + + Await TestStreamingFeature(input, host) + End Function + + + Public Async Function TestStaticAbstractEventViaAPI1(host As TestHost) As Task + Dim input = + + + +interface I3 +{ + abstract static event System.Action {|Definition:E3|}; +} + +class C3_1 : I3 +{ + public static event System.Action {|Definition:E3|}; +} + +class C3_2 : I3 +{ + static event System.Action I3.{|Definition:E$$3|} + { + add { } + remove { } + } +} + + + Await TestAPI(input, host) + End Function + + + Public Async Function TestStaticAbstractEventViaAPI2(host As TestHost) As Task + Dim input = + + + +interface I3 +{ + abstract static event System.Action {|Definition:E3|}; +} + +class C3_1 : I3 +{ + public static event System.Action {|Definition:E$$3|}; +} + +class C3_2 : I3 +{ + static event System.Action I3.{|Definition:E3|} + { + add { } + remove { } + } +} + + + Await TestAPI(input, host) + End Function End Class End Namespace diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb index 68ce3668624f5..9faa9028e087d 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PropertySymbols.vb @@ -1084,5 +1084,135 @@ namespace ConsoleApplication22 Await TestAPIAndFeature(input, kind, host) End Function + + + Public Async Function TestCSharp_AbstractStaticPropertyInInterface(kind As TestKind, host As TestHost) As Task + Dim input = + + + +interface I2 +{ + abstract static int {|Definition:P$$2|} { get; set; } +} + +class C2_1 : I2 +{ + public static int {|Definition:P2|} { get; set; } +} + +class C2_2 : I2 +{ + static int I2.{|Definition:P2|} { get; set; } +} + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + Public Async Function TestCSharp_AbstractStaticPropertyViaFeature1(host As TestHost) As Task + Dim input = + + + +interface I2 +{ + abstract static int {|Definition:P2|} { get; set; } +} + +class C2_1 : I2 +{ + public static int {|Definition:P$$2|} { get; set; } +} + +class C2_2 : I2 +{ + static int I2.P2 { get; set; } +} + + + + Await TestStreamingFeature(input, host) + End Function + + + Public Async Function TestCSharp_AbstractStaticPropertyViaFeature2(host As TestHost) As Task + Dim input = + + + +interface I2 +{ + abstract static int {|Definition:P2|} { get; set; } +} + +class C2_1 : I2 +{ + public static int P2 { get; set; } +} + +class C2_2 : I2 +{ + static int I2.{|Definition:P$$2|} { get; set; } +} + + + + Await TestStreamingFeature(input, host) + End Function + + + Public Async Function TestCSharp_AbstractStaticPropertyViaAPI1(host As TestHost) As Task + Dim input = + + + +interface I2 +{ + abstract static int {|Definition:P2|} { get; set; } +} + +class C2_1 : I2 +{ + public static int {|Definition:P2|} { get; set; } +} + +class C2_2 : I2 +{ + static int I2.{|Definition:P$$2|} { get; set; } +} + + + + Await TestAPI(input, host) + End Function + + + Public Async Function TestCSharp_AbstractStaticPropertyViaAPI2(host As TestHost) As Task + Dim input = + + + +interface I2 +{ + abstract static int {|Definition:P2|} { get; set; } +} + +class C2_1 : I2 +{ + public static int {|Definition:P$$2|} { get; set; } +} + +class C2_2 : I2 +{ + static int I2.{|Definition:P2|} { get; set; } +} + + + + Await TestAPI(input, host) + End Function End Class End Namespace From 940665aff3dd89cb65f41151765474dc9992a35b Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Fri, 11 Jun 2021 17:02:13 -0700 Subject: [PATCH 114/127] Let operator also try finding up/down via the inheritance chain --- .../FindReferences/Finders/OperatorSymbolReferenceFinder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OperatorSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OperatorSymbolReferenceFinder.cs index 86b07956abb0b..901b9ad3be443 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OperatorSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OperatorSymbolReferenceFinder.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols.Finders { - internal class OperatorSymbolReferenceFinder : AbstractReferenceFinder + internal class OperatorSymbolReferenceFinder : AbstractMethodOrPropertyOrEventSymbolReferenceFinder { protected override bool CanFind(IMethodSymbol symbol) => symbol.MethodKind == MethodKind.UserDefinedOperator; From 9f8f76ccf0e59a1f942420509c7407b6fafff683 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Fri, 11 Jun 2021 18:17:09 -0700 Subject: [PATCH 115/127] Fix implicit conversion --- .../CSharpInheritanceMarginService.cs | 4 +- .../AbstractInheritanceMarginService.cs | 2 +- .../FindReferencesTests.OperatorSymbols.vb | 135 ++++++++++++++++++ ...ExplicitConversionSymbolReferenceFinder.cs | 4 +- .../Core/Extensions/ISymbolExtensions.cs | 3 +- 5 files changed, 143 insertions(+), 5 deletions(-) diff --git a/src/EditorFeatures/CSharp/InheritanceMargin/CSharpInheritanceMarginService.cs b/src/EditorFeatures/CSharp/InheritanceMargin/CSharpInheritanceMarginService.cs index 85897b9e0c8b4..c1b8330890e7a 100644 --- a/src/EditorFeatures/CSharp/InheritanceMargin/CSharpInheritanceMarginService.cs +++ b/src/EditorFeatures/CSharp/InheritanceMargin/CSharpInheritanceMarginService.cs @@ -44,7 +44,8 @@ protected override ImmutableArray GetMembers(IEnumerable SyntaxKind.PropertyDeclaration, SyntaxKind.EventDeclaration, SyntaxKind.IndexerDeclaration, - SyntaxKind.OperatorDeclaration)) + SyntaxKind.OperatorDeclaration, + SyntaxKind.ConversionOperatorDeclaration)) { builder.Add(member); } @@ -71,6 +72,7 @@ protected override SyntaxToken GetDeclarationToken(SyntaxNode declarationNode) TypeDeclarationSyntax baseTypeDeclarationNode => baseTypeDeclarationNode.Identifier, IndexerDeclarationSyntax indexerDeclarationNode => indexerDeclarationNode.ThisKeyword, OperatorDeclarationSyntax operatorDeclarationNode => operatorDeclarationNode.OperatorToken, + ConversionOperatorDeclarationSyntax conversionOperatorDeclarationNode => conversionOperatorDeclarationNode.Type.GetFirstToken(), // Shouldn't reach here since the input declaration nodes are coming from GetMembers() method above _ => throw ExceptionUtilities.UnexpectedValue(declarationNode), }; diff --git a/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs b/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs index 451623ab38c76..155985d1726cd 100644 --- a/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs +++ b/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs @@ -95,7 +95,7 @@ private static bool CanHaveInheritanceTarget(ISymbol symbol) if (symbol is IEventSymbol or IPropertySymbol or IMethodSymbol { - MethodKind: MethodKind.Ordinary or MethodKind.ExplicitInterfaceImplementation or MethodKind.UserDefinedOperator + MethodKind: MethodKind.Ordinary or MethodKind.ExplicitInterfaceImplementation or MethodKind.UserDefinedOperator or MethodKind.Conversion }) { return true; diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OperatorSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OperatorSymbols.vb index dabf62fca6418..4018d498c9640 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OperatorSymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OperatorSymbols.vb @@ -483,5 +483,140 @@ class B Await TestAPIAndFeature(input, kind, host) End Function + + Public Async Function TestCSharpStaticAbstractOperatorInInterface(kind As TestKind, host As TestHost) As Task + Dim input = + + + + where T : I5 +{ + abstract static implicit operator {|Definition:i$$nt|}(T x); +} + +class C5_1 : I5 +{ + public static implicit operator {|Definition:int|}(C5_1 x) => default; +} + +class C5_2 : I5 +{ + + static implicit I5.operator {|Definition:int|}(C5_2 x) => default; +}]]> + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + Public Async Function TestCSharpStaticAbstractOperatorViaFeature1(host As TestHost) As Task + Dim input = + + + + where T : I5 +{ + abstract static implicit operator {|Definition:int|}(T x); +} + +class C5_1 : I5 +{ + public static implicit operator {|Definition:i$$nt|}(C5_1 x) => default; +} + +class C5_2 : I5 +{ + static implicit I5.operator int(C5_2 x) => default; +}]]> + + + + Await TestStreamingFeature(input, host) + End Function + + + Public Async Function TestCSharpStaticAbstractOperatorViaFeature2(host As TestHost) As Task + Dim input = + + + + where T : I5 +{ + abstract static implicit operator {|Definition:int|}(T x); +} + +class C5_1 : I5 +{ + public static implicit operator int(C5_1 x) => default; +} + +class C5_2 : I5 +{ + static implicit I5.operator {|Definition:i$$nt|}(C5_2 x) => default; +}]]> + + + + Await TestStreamingFeature(input, host) + End Function + + + Public Async Function TestCSharpStaticAbstractOperatorViaAPI1(host As TestHost) As Task + Dim input = + + + + where T : I5 +{ + abstract static implicit operator {|Definition:int|}(T x); +} + +class C5_1 : I5 +{ + public static implicit operator {|Definition:int|}(C5_1 x) => default; +} + +class C5_2 : I5 +{ + static implicit I5.operator {|Definition:i$$nt|}(C5_2 x) => default; +}]]> + + + + Await TestAPI(input, host) + End Function + + + Public Async Function TestCSharpStaticAbstractOperatorViaAPI2(host As TestHost) As Task + Dim input = + + + + where T : I5 +{ + abstract static implicit operator {|Definition:int|}(T x); +} + +class C5_1 : I5 +{ + public static implicit operator {|Definition:in$$t|}(C5_1 x) => default; +} + +class C5_2 : I5 +{ + static implicit I5.operator {|Definition:int|}(C5_2 x) => default; +}]]> + + + + Await TestAPI(input, host) + End Function End Class End Namespace diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ExplicitConversionSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ExplicitConversionSymbolReferenceFinder.cs index 31ab46d43bc63..5ac344b258d8f 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ExplicitConversionSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ExplicitConversionSymbolReferenceFinder.cs @@ -12,10 +12,10 @@ namespace Microsoft.CodeAnalysis.FindSymbols.Finders { - internal partial class ExplicitConversionSymbolReferenceFinder : AbstractReferenceFinder + internal partial class ExplicitConversionSymbolReferenceFinder : AbstractMethodOrPropertyOrEventSymbolReferenceFinder { protected override bool CanFind(IMethodSymbol symbol) - => symbol is { MethodKind: MethodKind.Conversion, Name: WellKnownMemberNames.ExplicitConversionName } && + => symbol is { MethodKind: MethodKind.Conversion, Name: WellKnownMemberNames.ExplicitConversionName or WellKnownMemberNames.ImplicitConversionName } && GetUnderlyingNamedType(symbol.ReturnType) is not null; private static INamedTypeSymbol? GetUnderlyingNamedType(ITypeSymbol symbol) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs index 33ae3b59dbf05..74d4c40ee1fad 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs @@ -138,7 +138,8 @@ public static bool IsImplementableMember([NotNullWhen(returnValue: true)] this I if (methodSymbol.MethodKind == MethodKind.Ordinary || methodSymbol.MethodKind == MethodKind.PropertyGet || methodSymbol.MethodKind == MethodKind.PropertySet || - methodSymbol.MethodKind == MethodKind.UserDefinedOperator) + methodSymbol.MethodKind == MethodKind.UserDefinedOperator || + methodSymbol.MethodKind == MethodKind.Conversion) { return true; } From a37bda556c05ac55ad22e9eb993d2874b8faf20b Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Fri, 11 Jun 2021 18:36:58 -0700 Subject: [PATCH 116/127] Add operator tests --- .../FindReferencesTests.OperatorSymbols.vb | 145 +++++++++++++++++- 1 file changed, 140 insertions(+), 5 deletions(-) diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OperatorSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OperatorSymbols.vb index 4018d498c9640..22186fda29cef 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OperatorSymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OperatorSymbols.vb @@ -484,7 +484,7 @@ class B End Function - Public Async Function TestCSharpStaticAbstractOperatorInInterface(kind As TestKind, host As TestHost) As Task + Public Async Function TestCSharpStaticAbstractConversionOperatorInInterface(kind As TestKind, host As TestHost) As Task Dim input = @@ -512,7 +512,7 @@ class C5_2 : I5 End Function - Public Async Function TestCSharpStaticAbstractOperatorViaFeature1(host As TestHost) As Task + Public Async Function TestCSharpStaticAbstractConversionOperatorViaFeature1(host As TestHost) As Task Dim input = @@ -539,7 +539,7 @@ class C5_2 : I5 End Function - Public Async Function TestCSharpStaticAbstractOperatorViaFeature2(host As TestHost) As Task + Public Async Function TestCSharpStaticAbstractConversionOperatorViaFeature2(host As TestHost) As Task Dim input = @@ -566,7 +566,7 @@ class C5_2 : I5 End Function - Public Async Function TestCSharpStaticAbstractOperatorViaAPI1(host As TestHost) As Task + Public Async Function TestCSharpStaticAbstractConversionOperatorViaApi1(host As TestHost) As Task Dim input = @@ -593,7 +593,7 @@ class C5_2 : I5 End Function - Public Async Function TestCSharpStaticAbstractOperatorViaAPI2(host As TestHost) As Task + Public Async Function TestCSharpStaticAbstractConversionOperatorViaApi2(host As TestHost) As Task Dim input = @@ -618,5 +618,140 @@ class C5_2 : I5 Await TestAPI(input, host) End Function + + + Public Async Function TestCSharpStaticAbstractOperatorInInterface(kind As TestKind, host As TestHost) As Task + Dim input = + + + + where T : I4 +{ + abstract static int operator {|Definition:+$$|}(T x); +} + +class C4_1 : I4 +{ + public static int operator {|Definition:+|}(C4_1 x) => default; +} + +class C4_2 : I4 +{ + static int I4.operator {|Definition:+|}(C4_2 x) => default; +}]]> + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + Public Async Function TestCSharpStaticAbstractOperatorViaApi1(host As TestHost) As Task + Dim input = + + + + where T : I4 +{ + abstract static int operator {|Definition:+|}(T x); +} + +class C4_1 : I4 +{ + public static int operator {|Definition:$$+|}(C4_1 x) => default; +} + +class C4_2 : I4 +{ + static int I4.operator {|Definition:+|}(C4_2 x) => default; +}]]> + + + + Await TestAPI(input, host) + End Function + + + Public Async Function TestCSharpStaticAbstractOperatorViaApi2(host As TestHost) As Task + Dim input = + + + + where T : I4 +{ + abstract static int operator {|Definition:+|}(T x); +} + +class C4_1 : I4 +{ + public static int operator {|Definition:+|}(C4_1 x) => default; +} + +class C4_2 : I4 +{ + static int I4.operator {|Definition:$$+|}(C4_2 x) => default; +}]]> + + + + Await TestAPI(input, host) + End Function + + + Public Async Function TestCSharpStaticAbstractOperatorViaFeature1(host As TestHost) As Task + Dim input = + + + + where T : I4 +{ + abstract static int operator {|Definition:+|}(T x); +} + +class C4_1 : I4 +{ + public static int operator {|Definition:$$+|}(C4_1 x) => default; +} + +class C4_2 : I4 +{ + static int I4.operator +(C4_2 x) => default; +}]]> + + + + Await TestStreamingFeature(input, host) + End Function + + + Public Async Function TestCSharpStaticAbstractOperatorViaFeature2(host As TestHost) As Task + Dim input = + + + + where T : I4 +{ + abstract static int operator {|Definition:+|}(T x); +} + +class C4_1 : I4 +{ + public static int operator +(C4_1 x) => default; +} + +class C4_2 : I4 +{ + static int I4.operator {|Definition:$$+|}(C4_2 x) => default; +}]]> + + + + Await TestStreamingFeature(input, host) + End Function End Class End Namespace From 5e17854fee3a9a6485b7275df0951fc24f958b4a Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Fri, 11 Jun 2021 20:40:38 -0700 Subject: [PATCH 117/127] Complete InheritanceMargin Test --- .../InheritanceMarginTests.cs | 121 ++++++++++++------ .../FindReferencesTests.OperatorSymbols.vb | 1 - 2 files changed, 84 insertions(+), 38 deletions(-) diff --git a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs index 25f8c425ec7c2..4903c8afda7e9 100644 --- a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs +++ b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs @@ -1048,42 +1048,26 @@ abstract class {|target1:AbsBar|} : IBar itemForFooInAbsBar); } -// [Fact] -// public Task Test() -// { -// var markup = @" -//interface {|target:I1|} where T : I1 -//{ -// static abstract int operator +(T i1); -//} - -//public class {|target1:Class1|} : I1 -//{ -// public static int operator +(Class1 i) => 1; -//}"; -// return VerifyInSingleDocumentAsync( -// markup, -// LanguageNames.CSharp); -// } - [Fact] public Task TestStaticAbstractMemberInterface() { var markup = @" -interface {|target:I1|} where T : I1 +interface {|target5:I1|} where T : I1 { - static abstract void M1(); - static abstract int P1 { get; set; } - static abstract event EventHandler e1; - static abstract int operator +(T i1); + static abstract void {|target4:M1|}(); + static abstract int {|target7:P1|} { get; set; } + static abstract event EventHandler {|target9:e1|}; + static abstract int operator {|target11:+|}(T i1); + static abstract implicit operator {|target12:int|}(T i1); } public class {|target1:Class1|} : I1 { - public static int {|target3:P1|} { get => 1; set { } } - public static event EventHandler {|target4:e1|}; public static void {|target2:M1|}() {} - public static int operator +(C1 i) => 1; + public static int {|target6:P1|} { get => 1; set { } } + public static event EventHandler {|target8:e1|}; + public static int operator {|target10:+|}(Class1 i) => 1; + public static implicit operator {|target13:int|}(Class1 i) => 0; }"; var itemForI1 = new TestInheritanceMemberItem( lineNumber: 2, @@ -1097,32 +1081,88 @@ public class {|target1:Class1|} : I1 lineNumber: 4, memberName: "void I1.M1()", targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "void Class1.M1()", + targetSymbolDisplayName: "static void Class1.M1()", locationTag: "target2", relationship: InheritanceRelationship.Implemented))); + var itemForAbsClass1 = new TestInheritanceMemberItem( + lineNumber: 11, + memberName: "class Class1", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "interface I1", + locationTag: "target5", + relationship: InheritanceRelationship.Implementing))); + + var itemForM1InClass1 = new TestInheritanceMemberItem( + lineNumber: 13, + memberName: "static void Class1.M1()", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "void I1.M1()", + locationTag: "target4", + relationship: InheritanceRelationship.Implementing))); + var itemForP1InI1 = new TestInheritanceMemberItem( lineNumber: 5, memberName: "int I1.P1 { get; set; }", targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "void I1.P1 { get; set; }", - locationTag: "target3", + targetSymbolDisplayName: "static int Class1.P1 { get; set; }", + locationTag: "target6", relationship: InheritanceRelationship.Implemented))); + var itemForP1InClass1 = new TestInheritanceMemberItem( + lineNumber: 14, + memberName: "static int Class1.P1 { get; set; }", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "int I1.P1 { get; set; }", + locationTag: "target7", + relationship: InheritanceRelationship.Implementing))); + var itemForE1InI1 = new TestInheritanceMemberItem( lineNumber: 6, memberName: "event EventHandler I1.e1", targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "event EventHandler Class1.e1", - locationTag: "target4", + targetSymbolDisplayName: "static event EventHandler Class1.e1", + locationTag: "target8", relationship: InheritanceRelationship.Implemented))); - var itemForAbsClass1 = new TestInheritanceMemberItem( - lineNumber: 10, - memberName: "class Class1", + var itemForE1InClass1 = new TestInheritanceMemberItem( + lineNumber: 15, + memberName: "static event EventHandler Class1.e1", targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "interface I1", - locationTag: "target", + targetSymbolDisplayName: "event EventHandler I1.e1", + locationTag: "target9", + relationship: InheritanceRelationship.Implementing))); + + var itemForPlusOperatorInI1 = new TestInheritanceMemberItem( + lineNumber: 7, + memberName: "int I1.operator +(T)", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "static int Class1.operator +(Class1)", + locationTag: "target10", + relationship: InheritanceRelationship.Implemented))); + + var itemForPlusOperatorInClass1 = new TestInheritanceMemberItem( + lineNumber: 16, + memberName: "static int Class1.operator +(Class1)", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "int I1.operator +(T)", + locationTag: "target11", + relationship: InheritanceRelationship.Implementing))); + + var itemForIntOperatorInI1 = new TestInheritanceMemberItem( + lineNumber: 8, + memberName: "I1.implicit operator int(T)", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "static Class1.implicit operator int(Class1)", + locationTag: "target13", + relationship: InheritanceRelationship.Implemented))); + + var itemForIntOperatorInClass1 = new TestInheritanceMemberItem( + lineNumber: 17, + memberName: "static Class1.implicit operator int(Class1)", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "I1.implicit operator int(T)", + locationTag: "target12", relationship: InheritanceRelationship.Implementing))); return VerifyInSingleDocumentAsync( @@ -1131,8 +1171,15 @@ public class {|target1:Class1|} : I1 itemForI1, itemForAbsClass1, itemForM1InI1, + itemForM1InClass1, itemForP1InI1, - itemForE1InI1); + itemForP1InClass1, + itemForE1InI1, + itemForE1InClass1, + itemForPlusOperatorInI1, + itemForPlusOperatorInClass1, + itemForIntOperatorInI1, + itemForIntOperatorInClass1); } #endregion diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OperatorSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OperatorSymbols.vb index 22186fda29cef..847bd34707da8 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OperatorSymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.OperatorSymbols.vb @@ -502,7 +502,6 @@ class C5_1 : I5 class C5_2 : I5 { - static implicit I5.operator {|Definition:int|}(C5_2 x) => default; }]]> From 8924bcbebb048b009f1fb839b32dd8782e6350e7 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Mon, 14 Jun 2021 23:11:26 -0700 Subject: [PATCH 118/127] Resolve comments --- .../InheritanceMarginServiceHelpers.cs | 5 ++--- .../InheritanceMargin/InheritanceRelationship.cs | 6 ++++-- .../InheritanceMarginHelpers.cs | 16 ++++++++-------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs b/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs index 660040c34312d..014b1e151591a 100644 --- a/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs +++ b/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -18,9 +19,6 @@ namespace Microsoft.CodeAnalysis.InheritanceMargin { internal static class InheritanceMarginServiceHelper { - /// - /// Display format used to generated the target's diplay name. - /// private static readonly SymbolDisplayFormat s_displayFormat = new( globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining, typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes, @@ -141,6 +139,7 @@ private static async ValueTask AddInheritanceMemberItemsForNamedTypeAsync( } else { + Debug.Assert(memberSymbol.TypeKind is TypeKind.Class or TypeKind.Struct); var item = await CreateInheritanceItemForClassAndStructureAsync( solution, memberSymbol, diff --git a/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs b/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs index d1e44db5c6777..7715f132eb745 100644 --- a/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs +++ b/src/Features/Core/Portable/InheritanceMargin/InheritanceRelationship.cs @@ -48,7 +48,7 @@ internal enum InheritanceRelationship ImplementedMember = 32, /// - /// Overriden member for member in class or structure. Shown as O↑ + /// Overridden member for member in class or structure. Shown as O↑ /// OverriddenMember = 64, @@ -57,7 +57,9 @@ internal enum InheritanceRelationship /// OverridingMember = 128, - // Implmenting member for member in interface. Shown as I↓ + /// + /// Implmenting member for member in interface. Shown as I↓ + /// ImplementingMember = 256 } } diff --git a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs index a69dec48d131c..d9a665b4283c7 100644 --- a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs +++ b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceMarginHelpers.cs @@ -16,24 +16,24 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.InheritanceMarg { internal static class InheritanceMarginHelpers { - private static readonly ImmutableHashSet s_relationships_Shown_As_I_Up_Arrow - = ImmutableHashSet.Empty + private static readonly ImmutableArray s_relationships_Shown_As_I_Up_Arrow + = ImmutableArray.Empty .Add(InheritanceRelationship.ImplementedInterface) .Add(InheritanceRelationship.InheritedInterface) .Add(InheritanceRelationship.ImplementedMember); - private static readonly ImmutableHashSet s_relationships_Shown_As_I_Down_Arrow - = ImmutableHashSet.Empty + private static readonly ImmutableArray s_relationships_Shown_As_I_Down_Arrow + = ImmutableArray.Empty .Add(InheritanceRelationship.ImplementingType) .Add(InheritanceRelationship.ImplementingMember); - private static readonly ImmutableHashSet s_relationships_Shown_As_O_Up_Arrow - = ImmutableHashSet.Empty + private static readonly ImmutableArray s_relationships_Shown_As_O_Up_Arrow + = ImmutableArray.Empty .Add(InheritanceRelationship.BaseType) .Add(InheritanceRelationship.OverriddenMember); - private static readonly ImmutableHashSet s_relationships_Shown_As_O_Down_Arrow - = ImmutableHashSet.Empty + private static readonly ImmutableArray s_relationships_Shown_As_O_Down_Arrow + = ImmutableArray.Empty .Add(InheritanceRelationship.DerivedType) .Add(InheritanceRelationship.OverridingMember); From 4620bdddae4f222f51d87a2e7399dba2f3a584b4 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Tue, 15 Jun 2021 08:19:48 -0700 Subject: [PATCH 119/127] Pack error codes. --- .../CSharp/Portable/Errors/ErrorCode.cs | 27 +- .../StaticAbstractMembersInInterfacesTests.cs | 468 +++++++++--------- 2 files changed, 248 insertions(+), 247 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 872f212512654..25b933787246f 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1941,21 +1941,22 @@ internal enum ErrorCode ERR_CannotInferDelegateType = 8917, ERR_InvalidNameInSubpattern = 8918, + ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces = 8919, + ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers = 8920, + ERR_BadAbstractUnaryOperatorSignature = 8921, + ERR_BadAbstractIncDecSignature = 8922, + ERR_BadAbstractIncDecRetType = 8923, + ERR_BadAbstractBinaryOperatorSignature = 8924, + ERR_BadAbstractShiftOperatorSignature = 8925, + ERR_BadAbstractStaticMemberAccess = 8926, + ERR_ExpressionTreeContainsAbstractStaticMemberAccess = 8927, + ERR_CloseUnimplementedInterfaceMemberNotStatic = 8928, + ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember = 8929, + ERR_ExplicitImplementationOfOperatorsMustBeStatic = 8930, + ERR_AbstractConversionNotInvolvingContainedType = 8931, + #endregion - ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces = 9100, - ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers = 9101, - ERR_BadAbstractUnaryOperatorSignature = 9102, - ERR_BadAbstractIncDecSignature = 9103, - ERR_BadAbstractIncDecRetType = 9104, - ERR_BadAbstractBinaryOperatorSignature = 9105, - ERR_BadAbstractShiftOperatorSignature = 9106, - ERR_BadAbstractStaticMemberAccess = 9107, - ERR_ExpressionTreeContainsAbstractStaticMemberAccess = 9108, - ERR_CloseUnimplementedInterfaceMemberNotStatic = 9109, - ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember = 9110, - ERR_ExplicitImplementationOfOperatorsMustBeStatic = 9111, - ERR_AbstractConversionNotInvolvingContainedType = 9112, // Note: you will need to re-generate compiler code after adding warnings (eng\generate-compiler-code.cmd) } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index a6badda285516..fd5ab55bf8be0 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -4547,7 +4547,7 @@ interface I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation1.VerifyDiagnostics( - // (4,26): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (4,26): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static void M01(); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(4, 26) ); @@ -4675,7 +4675,7 @@ interface I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation1.VerifyDiagnostics( - // (4,33): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (4,33): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static I1 operator + (I1 x); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(4, 31 + type.Length) ); @@ -4703,28 +4703,28 @@ interface I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation1.VerifyDiagnostics( - // (4,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (4,35): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static bool operator true (I1 x); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "true").WithLocation(4, 35), - // (5,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (5,35): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static bool operator false (I1 x); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "false").WithLocation(5, 35), - // (6,33): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,33): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static I1 operator > (I1 x, I1 y); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, ">").WithLocation(6, 33), - // (7,33): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (7,33): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static I1 operator < (I1 x, I1 y); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "<").WithLocation(7, 33), - // (8,33): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (8,33): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static I1 operator >= (I1 x, I1 y); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, ">=").WithLocation(8, 33), - // (9,33): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (9,33): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static I1 operator <= (I1 x, I1 y); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "<=").WithLocation(9, 33), - // (10,33): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (10,33): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static I1 operator == (I1 x, I1 y); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "==").WithLocation(10, 33), - // (11,33): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (11,33): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static I1 operator != (I1 x, I1 y); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "!=").WithLocation(11, 33) ); @@ -4784,10 +4784,10 @@ interface I1 where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation1.VerifyDiagnostics( - // (4,39): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (4,39): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static implicit operator int(T x); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "int").WithLocation(4, 39), - // (5,39): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (5,39): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static explicit operator T(int x); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "T").WithLocation(5, 39) ); @@ -4853,10 +4853,10 @@ interface I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation1.VerifyDiagnostics( - // (4,31): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (4,31): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static int P01 { get; set; } Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "get").WithLocation(4, 31), - // (4,36): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (4,36): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static int P01 { get; set; } Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "set").WithLocation(4, 36) ); @@ -4922,7 +4922,7 @@ interface I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation1.VerifyDiagnostics( - // (4,41): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (4,41): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static event System.Action E01; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "E01").WithLocation(4, 41) ); @@ -5027,34 +5027,34 @@ void Test(C8 x) references: new[] { compilation1.ToMetadataReference() }); var expected = new[] { - // (4,22): error CS9101: The interface 'I2' cannot be used as type parameter 'T1' in the generic type or method 'C1'. The constraint interface 'I1' or its base interface has static abstract members. + // (4,22): error CS8920: The interface 'I2' cannot be used as type parameter 'T1' in the generic type or method 'C1'. The constraint interface 'I1' or its base interface has static abstract members. // void Test(C1 x) Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "x").WithArguments("C1", "I1", "T1", "I2").WithLocation(4, 22), - // (15,11): error CS9101: The interface 'I2' cannot be used as type parameter 'T2' in the generic type or method 'C2.M()'. The constraint interface 'I1' or its base interface has static abstract members. + // (15,11): error CS8920: The interface 'I2' cannot be used as type parameter 'T2' in the generic type or method 'C2.M()'. The constraint interface 'I1' or its base interface has static abstract members. // x.M(); Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "M").WithArguments("C2.M()", "I1", "T2", "I2").WithLocation(15, 11), - // (21,22): error CS9101: The interface 'I2' cannot be used as type parameter 'T3' in the generic type or method 'C3'. The constraint interface 'I2' or its base interface has static abstract members. + // (21,22): error CS8920: The interface 'I2' cannot be used as type parameter 'T3' in the generic type or method 'C3'. The constraint interface 'I2' or its base interface has static abstract members. // void Test(C3 x, C3 y) Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "x").WithArguments("C3", "I2", "T3", "I2").WithLocation(21, 22), - // (21,32): error CS9101: The interface 'I3' cannot be used as type parameter 'T3' in the generic type or method 'C3'. The constraint interface 'I2' or its base interface has static abstract members. + // (21,32): error CS8920: The interface 'I3' cannot be used as type parameter 'T3' in the generic type or method 'C3'. The constraint interface 'I2' or its base interface has static abstract members. // void Test(C3 x, C3 y) Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "y").WithArguments("C3", "I2", "T3", "I3").WithLocation(21, 32), - // (32,11): error CS9101: The interface 'I2' cannot be used as type parameter 'T4' in the generic type or method 'C4.M()'. The constraint interface 'I2' or its base interface has static abstract members. + // (32,11): error CS8920: The interface 'I2' cannot be used as type parameter 'T4' in the generic type or method 'C4.M()'. The constraint interface 'I2' or its base interface has static abstract members. // x.M(); Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "M").WithArguments("C4.M()", "I2", "T4", "I2").WithLocation(32, 11), - // (33,11): error CS9101: The interface 'I3' cannot be used as type parameter 'T4' in the generic type or method 'C4.M()'. The constraint interface 'I2' or its base interface has static abstract members. + // (33,11): error CS8920: The interface 'I3' cannot be used as type parameter 'T4' in the generic type or method 'C4.M()'. The constraint interface 'I2' or its base interface has static abstract members. // x.M(); Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "M").WithArguments("C4.M()", "I2", "T4", "I3").WithLocation(33, 11), - // (39,22): error CS9101: The interface 'I3' cannot be used as type parameter 'T5' in the generic type or method 'C5'. The constraint interface 'I3' or its base interface has static abstract members. + // (39,22): error CS8920: The interface 'I3' cannot be used as type parameter 'T5' in the generic type or method 'C5'. The constraint interface 'I3' or its base interface has static abstract members. // void Test(C5 y) Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "y").WithArguments("C5", "I3", "T5", "I3").WithLocation(39, 22), - // (50,11): error CS9101: The interface 'I3' cannot be used as type parameter 'T6' in the generic type or method 'C6.M()'. The constraint interface 'I3' or its base interface has static abstract members. + // (50,11): error CS8920: The interface 'I3' cannot be used as type parameter 'T6' in the generic type or method 'C6.M()'. The constraint interface 'I3' or its base interface has static abstract members. // x.M(); Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "M").WithArguments("C6.M()", "I3", "T6", "I3").WithLocation(50, 11), - // (56,22): error CS9101: The interface 'I1' cannot be used as type parameter 'T7' in the generic type or method 'C7'. The constraint interface 'I1' or its base interface has static abstract members. + // (56,22): error CS8920: The interface 'I1' cannot be used as type parameter 'T7' in the generic type or method 'C7'. The constraint interface 'I1' or its base interface has static abstract members. // void Test(C7 y) Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "y").WithArguments("C7", "I1", "T7", "I1").WithLocation(56, 22), - // (67,11): error CS9101: The interface 'I1' cannot be used as type parameter 'T8' in the generic type or method 'C8.M()'. The constraint interface 'I1' or its base interface has static abstract members. + // (67,11): error CS8920: The interface 'I1' cannot be used as type parameter 'T8' in the generic type or method 'C8.M()'. The constraint interface 'I1' or its base interface has static abstract members. // x.M(); Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "M").WithArguments("C8.M()", "I1", "T8", "I1").WithLocation(67, 11) }; @@ -5345,22 +5345,22 @@ interface I13 // (9,26): error CS0562: The parameter of a unary operator must be the containing type // static bool operator +(T2? x) => throw null; Diagnostic(ErrorCode.ERR_BadUnaryOperatorSignature, op).WithLocation(9, 26), - // (26,39): error CS9102: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + // (26,39): error CS8921: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator +(T5 x); Diagnostic(ErrorCode.ERR_BadAbstractUnaryOperatorSignature, op).WithLocation(26, 39), - // (32,35): error CS9102: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + // (32,35): error CS8921: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator +(T71 x); Diagnostic(ErrorCode.ERR_BadAbstractUnaryOperatorSignature, op).WithLocation(32, 35), - // (37,35): error CS9102: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + // (37,35): error CS8921: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator +(T8 x); Diagnostic(ErrorCode.ERR_BadAbstractUnaryOperatorSignature, op).WithLocation(37, 35), - // (44,35): error CS9102: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + // (44,35): error CS8921: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator +(T10 x); Diagnostic(ErrorCode.ERR_BadAbstractUnaryOperatorSignature, op).WithLocation(44, 35), // (47,18): error CS0535: 'C11' does not implement interface member 'I10.operator false(T11)' // class C11 : I10 where T11 : C11 {} Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I10").WithArguments("C11", "I10.operator " + op + "(T11)").WithLocation(47, 18), - // (51,35): error CS9102: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + // (51,35): error CS8921: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator false(int x); Diagnostic(ErrorCode.ERR_BadAbstractUnaryOperatorSignature, op).WithLocation(51, 35) ); @@ -5441,22 +5441,22 @@ interface I13 // (9,25): error CS0559: The parameter type for ++ or -- operator must be the containing type // static T2? operator ++(T2? x) => throw null; Diagnostic(ErrorCode.ERR_BadIncDecSignature, op).WithLocation(9, 25), - // (26,37): error CS9103: The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + // (26,37): error CS8922: The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. // static abstract T5 operator ++(T5 x); Diagnostic(ErrorCode.ERR_BadAbstractIncDecSignature, op).WithLocation(26, 37), - // (32,34): error CS9103: The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + // (32,34): error CS8922: The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. // static abstract T71 operator ++(T71 x); Diagnostic(ErrorCode.ERR_BadAbstractIncDecSignature, op).WithLocation(32, 34), - // (37,33): error CS9103: The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + // (37,33): error CS8922: The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. // static abstract T8 operator ++(T8 x); Diagnostic(ErrorCode.ERR_BadAbstractIncDecSignature, op).WithLocation(37, 33), - // (44,34): error CS9103: The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + // (44,34): error CS8922: The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. // static abstract T10 operator ++(T10 x); Diagnostic(ErrorCode.ERR_BadAbstractIncDecSignature, op).WithLocation(44, 34), // (47,18): error CS0535: 'C11' does not implement interface member 'I10.operator --(T11)' // class C11 : I10 where T11 : C11 {} Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I10").WithArguments("C11", "I10.operator " + op + "(T11)").WithLocation(47, 18), - // (51,34): error CS9103: The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. + // (51,34): error CS8922: The parameter type for ++ or -- operator must be the containing type, or its type parameter constrained to it. // static abstract int operator ++(int x); Diagnostic(ErrorCode.ERR_BadAbstractIncDecSignature, op).WithLocation(51, 34) ); @@ -5548,34 +5548,34 @@ interface I15 where T151 : I15 where T152 : I15 x) => throw null; Diagnostic(ErrorCode.ERR_BadIncDecRetType, op).WithLocation(9, 25), - // (19,34): error CS9104: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + // (19,34): error CS8923: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. // static abstract T4? operator ++(I4 x); Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(19, 34), - // (26,37): error CS9104: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + // (26,37): error CS8923: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. // static abstract T5 operator ++(I6 x); Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(26, 37), - // (32,34): error CS9104: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + // (32,34): error CS8923: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. // static abstract T71 operator ++(I7 x); Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(32, 34), - // (37,33): error CS9104: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + // (37,33): error CS8923: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. // static abstract T8 operator ++(I8 x); Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(37, 33), - // (44,34): error CS9104: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + // (44,34): error CS8923: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. // static abstract T10 operator ++(I10 x); Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(44, 34), // (47,18): error CS0535: 'C11' does not implement interface member 'I10.operator ++(I10)' // class C11 : I10 where T11 : C11 {} Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I10").WithArguments("C11", "I10.operator " + op + "(I10)").WithLocation(47, 18), - // (51,34): error CS9104: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + // (51,34): error CS8923: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. // static abstract int operator ++(I12 x); Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(51, 34), - // (56,35): error CS9104: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + // (56,35): error CS8923: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. // static abstract T13? operator ++(T13 x); Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(56, 35), - // (61,34): error CS9104: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + // (61,34): error CS8923: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. // static abstract T14 operator ++(T14? x); Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(61, 34), - // (66,35): error CS9104: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. + // (66,35): error CS8923: The return type for ++ or -- operator must either match the parameter type, or be derived from the parameter type, or be the containing type's type parameter constrained to it unless the parameter type is a different type parameter. // static abstract T151 operator ++(T152 x); Diagnostic(ErrorCode.ERR_BadAbstractIncDecRetType, op).WithLocation(66, 35) ); @@ -5655,22 +5655,22 @@ interface I13 // (9,26): error CS0563: One of the parameters of a binary operator must be the containing type // static bool operator +(T2? x, bool y) => throw null; Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, op).WithLocation(9, 26), - // (26,39): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // (26,39): error CS8924: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator +(T5 x, bool y); Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(26, 39), - // (32,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // (32,35): error CS8924: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator +(T71 x, bool y); Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(32, 35), - // (37,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // (37,35): error CS8924: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator +(T8 x, bool y); Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(37, 35), - // (44,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // (44,35): error CS8924: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator +(T10 x, bool y); Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(44, 35), // (47,18): error CS0535: 'C11' does not implement interface member 'I10.operator /(T11, bool)' // class C11 : I10 where T11 : C11 {} Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I10").WithArguments("C11", "I10.operator " + op + "(T11, bool)").WithLocation(47, 18), - // (51,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // (51,35): error CS8924: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator +(int x, bool y); Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(51, 35) ); @@ -5750,22 +5750,22 @@ interface I13 // (9,26): error CS0563: One of the parameters of a binary operator must be the containing type // static bool operator +(bool y, T2? x) => throw null; Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, op).WithLocation(9, 26), - // (26,39): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // (26,39): error CS8924: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator +(bool y, T5 x); Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(26, 39), - // (32,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // (32,35): error CS8924: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator +(bool y, T71 x); Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(32, 35), - // (37,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // (37,35): error CS8924: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator +(bool y, T8 x); Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(37, 35), - // (44,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // (44,35): error CS8924: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator +(bool y, T10 x); Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(44, 35), // (47,18): error CS0535: 'C11' does not implement interface member 'I10.operator <=(bool, T11)' // class C11 : I10 where T11 : C11 {} Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I10").WithArguments("C11", "I10.operator " + op + "(bool, T11)").WithLocation(47, 18), - // (51,35): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // (51,35): error CS8924: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. // static abstract bool operator +(bool y, int x); Diagnostic(ErrorCode.ERR_BadAbstractBinaryOperatorSignature, op).WithLocation(51, 35) ); @@ -5851,25 +5851,25 @@ interface I14 // (9,26): error CS0564: The first operand of an overloaded shift operator must have the same type as the containing type, and the type of the second operand must be int // static bool operator <<(T2? x, int y) => throw null; Diagnostic(ErrorCode.ERR_BadShiftOperatorSignature, op).WithLocation(9, 26), - // (26,39): error CS9106: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + // (26,39): error CS8925: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int // static abstract bool operator <<(T5 x, int y); Diagnostic(ErrorCode.ERR_BadAbstractShiftOperatorSignature, op).WithLocation(26, 39), - // (32,35): error CS9106: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + // (32,35): error CS8925: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int // static abstract bool operator <<(T71 x, int y); Diagnostic(ErrorCode.ERR_BadAbstractShiftOperatorSignature, op).WithLocation(32, 35), - // (37,35): error CS9106: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + // (37,35): error CS8925: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int // static abstract bool operator <<(T8 x, int y); Diagnostic(ErrorCode.ERR_BadAbstractShiftOperatorSignature, op).WithLocation(37, 35), - // (44,35): error CS9106: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + // (44,35): error CS8925: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int // static abstract bool operator <<(T10 x, int y); Diagnostic(ErrorCode.ERR_BadAbstractShiftOperatorSignature, op).WithLocation(44, 35), // (47,18): error CS0535: 'C11' does not implement interface member 'I10.operator >>(T11, int)' // class C11 : I10 where T11 : C11 {} Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I10").WithArguments("C11", "I10.operator " + op + "(T11, int)").WithLocation(47, 18), - // (51,35): error CS9106: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + // (51,35): error CS8925: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int // static abstract bool operator <<(int x, int y); Diagnostic(ErrorCode.ERR_BadAbstractShiftOperatorSignature, op).WithLocation(51, 35), - // (61,35): error CS9106: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int + // (61,35): error CS8925: The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it, and the type of the second operand must be int // static abstract bool operator <<(I14 x, bool y); Diagnostic(ErrorCode.ERR_BadAbstractShiftOperatorSignature, op).WithLocation(61, 35) ); @@ -5972,22 +5972,22 @@ interface I19 where T19_1 : I19, T19_2 // (9,39): error CS1964: 'I2.explicit operator dynamic(T2)': user-defined conversions to or from the dynamic type are not allowed // abstract static explicit operator dynamic(T2 y); Diagnostic(ErrorCode.ERR_BadDynamicConversion, "dynamic").WithArguments("I2." + op + " operator dynamic(T2)").WithLocation(9, 39), - // (26,43): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + // (26,43): error CS8931: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator T5 (bool y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "T5").WithLocation(26, 43), - // (32,39): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + // (32,39): error CS8931: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator T71 (bool y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "T71").WithLocation(32, 39), - // (37,39): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + // (37,39): error CS8931: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator T8(bool y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "T8").WithLocation(37, 39), - // (44,39): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + // (44,39): error CS8931: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator T10(bool y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "T10").WithLocation(44, 39), // (47,18): error CS0535: 'C11' does not implement interface member 'I10.explicit operator T11(bool)' // class C11 : I10 where T11 : C11 {} Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I10").WithArguments("C11", "I10." + op + " operator T11(bool)").WithLocation(47, 18), - // (51,39): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + // (51,39): error CS8931: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator int(bool y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "int").WithLocation(51, 39), // (56,39): error CS0552: 'I13.explicit operator I13(bool)': user-defined conversions to or from an interface are not allowed @@ -6093,22 +6093,22 @@ interface I19 where T19_1 : I19, T19_2 // (9,39): error CS1964: 'I2.explicit operator T2(dynamic)': user-defined conversions to or from the dynamic type are not allowed // abstract static explicit operator T2(dynamic y); Diagnostic(ErrorCode.ERR_BadDynamicConversion, "T2").WithArguments("I2." + op + " operator T2(dynamic)").WithLocation(9, 39), - // (26,43): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + // (26,43): error CS8931: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator bool(T5 y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "bool").WithLocation(26, 43), - // (32,39): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + // (32,39): error CS8931: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator bool(T71 y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "bool").WithLocation(32, 39), - // (37,39): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + // (37,39): error CS8931: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator bool(T8 y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "bool").WithLocation(37, 39), - // (44,39): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + // (44,39): error CS8931: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator bool(T10 y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "bool").WithLocation(44, 39), // (47,18): error CS0535: 'C11' does not implement interface member 'I10.explicit operator bool(T11)' // class C11 : I10 where T11 : C11 {} Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I10").WithArguments("C11", "I10." + op + " operator bool(T11)").WithLocation(47, 18), - // (51,39): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + // (51,39): error CS8931: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // static abstract explicit operator bool(int y); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "bool").WithLocation(51, 39), // (56,39): error CS0552: 'I13.explicit operator bool(I13)': user-defined conversions to or from an interface are not allowed @@ -6169,7 +6169,7 @@ static void MT2() where T : I1 targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (8,9): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (8,9): error CS8926: A static abstract interface member can be accessed only on a type parameter. // M01(); Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "M01").WithLocation(8, 9), // (14,9): error CS0176: Member 'I1.M01()' cannot be accessed with an instance reference; qualify it with a type name instead @@ -6178,7 +6178,7 @@ static void MT2() where T : I1 // (15,9): error CS0176: Member 'I1.M04()' cannot be accessed with an instance reference; qualify it with a type name instead // this.M04(); Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.M04").WithArguments("I1.M04()").WithLocation(15, 9), - // (27,9): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (27,9): error CS8926: A static abstract interface member can be accessed only on a type parameter. // I1.M01(); Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "I1.M01").WithLocation(27, 9), // (28,9): error CS0176: Member 'I1.M01()' cannot be accessed with an instance reference; qualify it with a type name instead @@ -6199,7 +6199,7 @@ static void MT2() where T : I1 // (38,11): error CS0122: 'I1.M05()' is inaccessible due to its protection level // T.M05(); Diagnostic(ErrorCode.ERR_BadAccess, "M05").WithArguments("I1.M05()").WithLocation(38, 11), - // (40,71): error CS9108: An expression tree may not contain an access of static abstract interface member + // (40,71): error CS8927: An expression tree may not contain an access of static abstract interface member // _ = (System.Linq.Expressions.Expression)(() => T.M01()); Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, "T.M01()").WithLocation(40, 71) ); @@ -6421,7 +6421,7 @@ static void M02() where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (6,9): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,9): error CS8919: Target runtime doesn't support static abstract members in interfaces. // T.M01(); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "T.M01").WithLocation(6, 9) ); @@ -6431,7 +6431,7 @@ static void M02() where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (12,26): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (12,26): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static void M01(); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(12, 26) ); @@ -6465,7 +6465,7 @@ static void M02() where T : I1 // (11,23): error CS0119: 'T' is a type parameter, which is not valid in the given context // _ = from t in T select t + 1; Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type parameter").WithLocation(11, 23), - // (12,26): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (12,26): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = from t in I1 select t + 1; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "select t + 1").WithLocation(12, 26) ); @@ -6566,17 +6566,17 @@ static void MT2() targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (8,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (8,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = -x; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, prefixOp + "x" + postfixOp).WithLocation(8, 13), - // (13,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (13,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = -y; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, prefixOp + "y" + postfixOp).WithLocation(13, 13), - // (21,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (21,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = -a; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, prefixOp + "a" + postfixOp).WithLocation(21, 13), (prefixOp + postfixOp).Length == 1 ? - // (26,78): error CS9108: An expression tree may not contain an access of static abstract interface member + // (26,78): error CS8927: An expression tree may not contain an access of static abstract interface member // _ = (System.Linq.Expressions.Expression>)((T b) => (-b).ToString()); Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, prefixOp + "b" + postfixOp).WithLocation(26, 78) : @@ -6988,7 +6988,7 @@ static void M02(T x) where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (6,13): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,13): error CS8919: Target runtime doesn't support static abstract members in interfaces. // _ = -x; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, prefixOp + "x" + postfixOp).WithLocation(6, 13) ); @@ -6998,7 +6998,7 @@ static void M02(T x) where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (12,32): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (12,32): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static T operator- (T x); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, prefixOp + postfixOp).WithLocation(12, 31) ); @@ -7097,16 +7097,16 @@ static void MT2() where T : I1 targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (9,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (9,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = x ? true : false; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "x").WithLocation(9, 13), - // (14,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (14,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = y ? true : false; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "y").WithLocation(14, 13), - // (22,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (22,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = a ? true : false; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "a").WithLocation(22, 13), - // (27,78): error CS9108: An expression tree may not contain an access of static abstract interface member + // (27,78): error CS8927: An expression tree may not contain an access of static abstract interface member // _ = (System.Linq.Expressions.Expression>)((T b) => (b ? true : false).ToString()); Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, "b").WithLocation(27, 78) ); @@ -7224,7 +7224,7 @@ static void M02(T x) where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (6,13): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,13): error CS8919: Target runtime doesn't support static abstract members in interfaces. // _ = x ? true : false; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "x").WithLocation(6, 13) ); @@ -7234,10 +7234,10 @@ static void M02(T x) where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (12,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (12,35): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static bool operator true (I1 x); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "true").WithLocation(12, 35), - // (13,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (13,35): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static bool operator false (I1 x); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "false").WithLocation(13, 35) ); @@ -7342,13 +7342,13 @@ class C targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (9,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (9,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = x == x; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "x " + op + " x").WithLocation(9, 13), - // (14,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (14,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = y == y; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "y " + op + " y").WithLocation(14, 13), - // (22,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (22,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = a == a; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "a " + op + " a").WithLocation(22, 13), // (27,98): error CS8382: An expression tree may not contain a tuple == or != operator @@ -7592,7 +7592,7 @@ class C references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (6,13): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,13): error CS8919: Target runtime doesn't support static abstract members in interfaces. // _ = x == x; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "x " + op + " x").WithLocation(6, 13) ); @@ -7602,10 +7602,10 @@ class C targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (21,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (21,35): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static bool operator true (I1 x); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "true").WithLocation(21, 35), - // (22,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (22,35): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static bool operator false (I1 x); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "false").WithLocation(22, 35) ); @@ -7724,16 +7724,16 @@ public partial interface I1 targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (8,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (8,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = x - 1; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "x " + op + " 1").WithLocation(8, 13), - // (13,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (13,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = y - 2; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "y " + op + " 2").WithLocation(13, 13), - // (21,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (21,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = a - 3; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "a " + op + " 3").WithLocation(21, 13), - // (26,78): error CS9108: An expression tree may not contain an access of static abstract interface member + // (26,78): error CS8927: An expression tree may not contain an access of static abstract interface member // _ = (System.Linq.Expressions.Expression>)((T b) => (b - 4).ToString()); Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, "b " + op + " 4").WithLocation(26, 78) ); @@ -7935,16 +7935,16 @@ .maxstack 8 var builder = ArrayBuilder.GetInstance(); builder.AddRange( - // (10,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (10,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = x && x; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "x " + op + op + " x").WithLocation(10, 13), - // (15,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (15,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = y && y; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "y " + op + op + " y").WithLocation(15, 13), - // (23,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (23,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = a && a; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "a " + op + op + " a").WithLocation(23, 13), - // (28,78): error CS9108: An expression tree may not contain an access of static abstract interface member + // (28,78): error CS8927: An expression tree may not contain an access of static abstract interface member // _ = (System.Linq.Expressions.Expression>)((T b) => (b && b).ToString()); Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, "b " + op + op + " b").WithLocation(28, 78) ); @@ -7952,7 +7952,7 @@ .maxstack 8 if (op == "&" ? falseIsAbstract : trueIsAbstract) { builder.Add( - // (33,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (33,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = b || c; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "b " + op + op + " c").WithLocation(33, 13) ); @@ -8012,13 +8012,13 @@ static void MT2() where T : I2 targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (8,9): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (8,9): error CS8926: A static abstract interface member can be accessed only on a type parameter. // x /= 1; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "x " + op + "= 1").WithLocation(8, 9), - // (13,9): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (13,9): error CS8926: A static abstract interface member can be accessed only on a type parameter. // y /= 2; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "y " + op + "= 2").WithLocation(13, 9), - // (26,9): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (26,9): error CS8926: A static abstract interface member can be accessed only on a type parameter. // a /= 3; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "a " + op + "= 3").WithLocation(26, 9), // (31,78): error CS0832: An expression tree may not contain an assignment operator @@ -8127,13 +8127,13 @@ static void MT2() where T : I1 targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (12,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (12,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = x == x; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "x " + op + " x").WithLocation(12, 13), - // (17,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (17,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = y == y; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "y " + op + " y").WithLocation(17, 13), - // (25,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (25,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = a == a; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "a " + op + " a").WithLocation(25, 13), // (30,92): error CS8382: An expression tree may not contain a tuple == or != operator @@ -9845,7 +9845,7 @@ static void M02(T x, int y) where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (6,13): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,13): error CS8919: Target runtime doesn't support static abstract members in interfaces. // _ = x - y; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "x " + op + " y").WithLocation(6, 13) ); @@ -9855,7 +9855,7 @@ static void M02(T x, int y) where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( - // (12,32): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (12,32): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static I1 operator- (I1 x, int y); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(12, 32) ); @@ -9907,7 +9907,7 @@ static void M02(T x, T y) where T : I1 else { compilation2.VerifyDiagnostics( - // (6,13): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,13): error CS8919: Target runtime doesn't support static abstract members in interfaces. // _ = x && y; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "x " + op + op + " y").WithLocation(6, 13) ); @@ -9922,7 +9922,7 @@ static void M02(T x, T y) where T : I1 if (binaryIsAbstract) { builder.Add( - // (12,32): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (12,32): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static I1 operator& (I1 x, I1 y); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(12, 32) ); @@ -9931,7 +9931,7 @@ static void M02(T x, T y) where T : I1 if (trueIsAbstract) { builder.Add( - // (13,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (13,35): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static bool operator true (I1 x); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "true").WithLocation(13, 35) ); @@ -9940,7 +9940,7 @@ static void M02(T x, T y) where T : I1 if (falseIsAbstract) { builder.Add( - // (14,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (14,35): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static bool operator false (I1 x); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "false").WithLocation(14, 35) ); @@ -9989,7 +9989,7 @@ static void M02(T x, int y) where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (6,9): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,9): error CS8919: Target runtime doesn't support static abstract members in interfaces. // x *= y; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "x " + op + "= y").WithLocation(6, 9) ); @@ -9999,7 +9999,7 @@ static void M02(T x, int y) where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (12,31): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (12,31): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static T operator* (T x, int y); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(12, 31) ); @@ -10037,7 +10037,7 @@ static void M02((int, T) x) where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (6,13): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,13): error CS8919: Target runtime doesn't support static abstract members in interfaces. // _ = x == x; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "x " + op + " x").WithLocation(6, 13) ); @@ -10047,10 +10047,10 @@ static void M02((int, T) x) where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (12,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (12,35): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static bool operator == (T x, T y); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "==").WithLocation(12, 35), - // (13,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (13,35): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static bool operator != (T x, T y); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "!=").WithLocation(13, 35) ); @@ -10350,7 +10350,7 @@ static void MT2() where T : I1 targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (8,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (8,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = P01; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "P01").WithLocation(8, 13), // (14,13): error CS0176: Member 'I1.P01' cannot be accessed with an instance reference; qualify it with a type name instead @@ -10359,7 +10359,7 @@ static void MT2() where T : I1 // (15,13): error CS0176: Member 'I1.P04' cannot be accessed with an instance reference; qualify it with a type name instead // _ = this.P04; Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.P04").WithArguments("I1.P04").WithLocation(15, 13), - // (27,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (27,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = I1.P01; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "I1.P01").WithLocation(27, 13), // (28,13): error CS0176: Member 'I1.P01' cannot be accessed with an instance reference; qualify it with a type name instead @@ -10380,7 +10380,7 @@ static void MT2() where T : I1 // (38,15): error CS0122: 'I1.P05' is inaccessible due to its protection level // _ = T.P05; Diagnostic(ErrorCode.ERR_BadAccess, "P05").WithArguments("I1.P05").WithLocation(38, 15), - // (40,71): error CS9108: An expression tree may not contain an access of static abstract interface member + // (40,71): error CS8927: An expression tree may not contain an access of static abstract interface member // _ = (System.Linq.Expressions.Expression)(() => T.P01.ToString()); Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, "T.P01").WithLocation(40, 71) ); @@ -10438,7 +10438,7 @@ static void MT2() where T : I1 targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (8,9): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (8,9): error CS8926: A static abstract interface member can be accessed only on a type parameter. // P01 = 1; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "P01").WithLocation(8, 9), // (14,9): error CS0176: Member 'I1.P01' cannot be accessed with an instance reference; qualify it with a type name instead @@ -10447,7 +10447,7 @@ static void MT2() where T : I1 // (15,9): error CS0176: Member 'I1.P04' cannot be accessed with an instance reference; qualify it with a type name instead // this.P04 = 1; Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.P04").WithArguments("I1.P04").WithLocation(15, 9), - // (27,9): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (27,9): error CS8926: A static abstract interface member can be accessed only on a type parameter. // I1.P01 = 1; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "I1.P01").WithLocation(27, 9), // (28,9): error CS0176: Member 'I1.P01' cannot be accessed with an instance reference; qualify it with a type name instead @@ -10471,7 +10471,7 @@ static void MT2() where T : I1 // (40,71): error CS0832: An expression tree may not contain an assignment operator // _ = (System.Linq.Expressions.Expression)(() => T.P01 = 1); Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAssignment, "T.P01 = 1").WithLocation(40, 71), - // (40,71): error CS9108: An expression tree may not contain an access of static abstract interface member + // (40,71): error CS8927: An expression tree may not contain an access of static abstract interface member // _ = (System.Linq.Expressions.Expression)(() => T.P01 = 1); Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, "T.P01").WithLocation(40, 71) ); @@ -10529,10 +10529,10 @@ static void MT2() where T : I1 targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (8,9): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (8,9): error CS8926: A static abstract interface member can be accessed only on a type parameter. // P01 += 1; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "P01").WithLocation(8, 9), - // (8,9): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (8,9): error CS8926: A static abstract interface member can be accessed only on a type parameter. // P01 += 1; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "P01").WithLocation(8, 9), // (14,9): error CS0176: Member 'I1.P01' cannot be accessed with an instance reference; qualify it with a type name instead @@ -10541,10 +10541,10 @@ static void MT2() where T : I1 // (15,9): error CS0176: Member 'I1.P04' cannot be accessed with an instance reference; qualify it with a type name instead // this.P04 += 1; Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.P04").WithArguments("I1.P04").WithLocation(15, 9), - // (27,9): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (27,9): error CS8926: A static abstract interface member can be accessed only on a type parameter. // I1.P01 += 1; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "I1.P01").WithLocation(27, 9), - // (27,9): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (27,9): error CS8926: A static abstract interface member can be accessed only on a type parameter. // I1.P01 += 1; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "I1.P01").WithLocation(27, 9), // (28,9): error CS0176: Member 'I1.P01' cannot be accessed with an instance reference; qualify it with a type name instead @@ -10568,7 +10568,7 @@ static void MT2() where T : I1 // (40,71): error CS0832: An expression tree may not contain an assignment operator // _ = (System.Linq.Expressions.Expression)(() => T.P01 += 1); Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAssignment, "T.P01 += 1").WithLocation(40, 71), - // (40,71): error CS9108: An expression tree may not contain an access of static abstract interface member + // (40,71): error CS8927: An expression tree may not contain an access of static abstract interface member // _ = (System.Linq.Expressions.Expression)(() => T.P01 += 1); Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, "T.P01").WithLocation(40, 71) ); @@ -10974,7 +10974,7 @@ static void M02() where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (6,13): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,13): error CS8919: Target runtime doesn't support static abstract members in interfaces. // _ = T.P01; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "T.P01").WithLocation(6, 13) ); @@ -10984,10 +10984,10 @@ static void M02() where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (12,31): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (12,31): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static int P01 { get; set; } Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "get").WithLocation(12, 31), - // (12,36): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (12,36): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static int P01 { get; set; } Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "set").WithLocation(12, 36) ); @@ -11023,7 +11023,7 @@ static void M02() where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (6,9): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,9): error CS8919: Target runtime doesn't support static abstract members in interfaces. // T.P01 = 1; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "T.P01").WithLocation(6, 9) ); @@ -11033,10 +11033,10 @@ static void M02() where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (12,31): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (12,31): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static int P01 { get; set; } Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "get").WithLocation(12, 31), - // (12,36): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (12,36): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static int P01 { get; set; } Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "set").WithLocation(12, 36) ); @@ -11072,10 +11072,10 @@ static void M02() where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (6,9): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,9): error CS8919: Target runtime doesn't support static abstract members in interfaces. // T.P01 += 1; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "T.P01").WithLocation(6, 9), - // (6,9): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,9): error CS8919: Target runtime doesn't support static abstract members in interfaces. // T.P01 += 1; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "T.P01").WithLocation(6, 9) ); @@ -11085,10 +11085,10 @@ static void M02() where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (12,31): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (12,31): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static int P01 { get; set; } Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "get").WithLocation(12, 31), - // (12,36): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (12,36): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static int P01 { get; set; } Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "set").WithLocation(12, 36) ); @@ -11293,25 +11293,25 @@ static void MT2() where T : I1 targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (8,9): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (8,9): error CS8926: A static abstract interface member can be accessed only on a type parameter. // P01 += null; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "P01 += null").WithLocation(8, 9), // (14,9): error CS0176: Member 'I1.P01' cannot be accessed with an instance reference; qualify it with a type name instead // this.P01 += null; Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.P01").WithArguments("I1.P01").WithLocation(14, 9), - // (14,9): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (14,9): error CS8926: A static abstract interface member can be accessed only on a type parameter. // this.P01 += null; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "this.P01 += null").WithLocation(14, 9), // (15,9): error CS0176: Member 'I1.P04' cannot be accessed with an instance reference; qualify it with a type name instead // this.P04 += null; Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.P04").WithArguments("I1.P04").WithLocation(15, 9), - // (27,9): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (27,9): error CS8926: A static abstract interface member can be accessed only on a type parameter. // I1.P01 += null; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "I1.P01 += null").WithLocation(27, 9), // (28,9): error CS0176: Member 'I1.P01' cannot be accessed with an instance reference; qualify it with a type name instead // x.P01 += null; Diagnostic(ErrorCode.ERR_ObjectProhibited, "x.P01").WithArguments("I1.P01").WithLocation(28, 9), - // (28,9): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (28,9): error CS8926: A static abstract interface member can be accessed only on a type parameter. // x.P01 += null; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "x.P01 += null").WithLocation(28, 9), // (30,9): error CS0176: Member 'I1.P04' cannot be accessed with an instance reference; qualify it with a type name instead @@ -11387,25 +11387,25 @@ static void MT2() where T : I1 targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (8,9): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (8,9): error CS8926: A static abstract interface member can be accessed only on a type parameter. // P01 -= null; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "P01 -= null").WithLocation(8, 9), // (14,9): error CS0176: Member 'I1.P01' cannot be accessed with an instance reference; qualify it with a type name instead // this.P01 -= null; Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.P01").WithArguments("I1.P01").WithLocation(14, 9), - // (14,9): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (14,9): error CS8926: A static abstract interface member can be accessed only on a type parameter. // this.P01 -= null; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "this.P01 -= null").WithLocation(14, 9), // (15,9): error CS0176: Member 'I1.P04' cannot be accessed with an instance reference; qualify it with a type name instead // this.P04 -= null; Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.P04").WithArguments("I1.P04").WithLocation(15, 9), - // (27,9): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (27,9): error CS8926: A static abstract interface member can be accessed only on a type parameter. // I1.P01 -= null; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "I1.P01 -= null").WithLocation(27, 9), // (28,9): error CS0176: Member 'I1.P01' cannot be accessed with an instance reference; qualify it with a type name instead // x.P01 -= null; Diagnostic(ErrorCode.ERR_ObjectProhibited, "x.P01").WithArguments("I1.P01").WithLocation(28, 9), - // (28,9): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (28,9): error CS8926: A static abstract interface member can be accessed only on a type parameter. // x.P01 -= null; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "x.P01 -= null").WithLocation(28, 9), // (30,9): error CS0176: Member 'I1.P04' cannot be accessed with an instance reference; qualify it with a type name instead @@ -11660,7 +11660,7 @@ static void M02() where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (6,9): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,9): error CS8919: Target runtime doesn't support static abstract members in interfaces. // T.P01 += null; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "T.P01 += null").WithLocation(6, 9) ); @@ -11670,7 +11670,7 @@ static void M02() where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (12,41): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (12,41): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static event System.Action P01; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "P01").WithLocation(12, 41) ); @@ -11706,7 +11706,7 @@ static void M02() where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (6,9): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,9): error CS8919: Target runtime doesn't support static abstract members in interfaces. // T.P01 -= null; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "T.P01 -= null").WithLocation(6, 9) ); @@ -11716,7 +11716,7 @@ static void M02() where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (12,41): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (12,41): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static event System.Action P01; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "P01").WithLocation(12, 41) ); @@ -12068,7 +12068,7 @@ static void MT2() where T : I1 targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (8,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (8,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = (System.Action)M01; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "(System.Action)M01").WithLocation(8, 13), // (14,28): error CS0176: Member 'I1.M01()' cannot be accessed with an instance reference; qualify it with a type name instead @@ -12077,7 +12077,7 @@ static void MT2() where T : I1 // (15,28): error CS0176: Member 'I1.M04()' cannot be accessed with an instance reference; qualify it with a type name instead // _ = (System.Action)this.M04; Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.M04").WithArguments("I1.M04()").WithLocation(15, 28), - // (27,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (27,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = (System.Action)I1.M01; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "(System.Action)I1.M01").WithLocation(27, 13), // (28,28): error CS0176: Member 'I1.M01()' cannot be accessed with an instance reference; qualify it with a type name instead @@ -12098,7 +12098,7 @@ static void MT2() where T : I1 // (38,30): error CS0122: 'I1.M05()' is inaccessible due to its protection level // _ = (System.Action)T.M05; Diagnostic(ErrorCode.ERR_BadAccess, "M05").WithArguments("I1.M05()").WithLocation(38, 30), - // (40,87): error CS9108: An expression tree may not contain an access of static abstract interface member + // (40,87): error CS8927: An expression tree may not contain an access of static abstract interface member // _ = (System.Linq.Expressions.Expression)(() => ((System.Action)T.M01).ToString()); Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, "T.M01").WithLocation(40, 87) ); @@ -12211,7 +12211,7 @@ static void M02() where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (6,13): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,13): error CS8919: Target runtime doesn't support static abstract members in interfaces. // _ = (System.Action)T.M01; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "(System.Action)T.M01").WithLocation(6, 13) ); @@ -12221,7 +12221,7 @@ static void M02() where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (12,26): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (12,26): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static void M01(); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(12, 26) ); @@ -12328,7 +12328,7 @@ static void MT2() where T : I1 targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (8,31): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (8,31): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = new System.Action(M01); Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "M01").WithLocation(8, 31), // (14,31): error CS0176: Member 'I1.M01()' cannot be accessed with an instance reference; qualify it with a type name instead @@ -12337,7 +12337,7 @@ static void MT2() where T : I1 // (15,31): error CS0176: Member 'I1.M04()' cannot be accessed with an instance reference; qualify it with a type name instead // _ = new System.Action(this.M04); Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.M04").WithArguments("I1.M04()").WithLocation(15, 31), - // (27,31): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (27,31): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = new System.Action(I1.M01); Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "I1.M01").WithLocation(27, 31), // (28,31): error CS0176: Member 'I1.M01()' cannot be accessed with an instance reference; qualify it with a type name instead @@ -12358,7 +12358,7 @@ static void MT2() where T : I1 // (38,33): error CS0122: 'I1.M05()' is inaccessible due to its protection level // _ = new System.Action(T.M05); Diagnostic(ErrorCode.ERR_BadAccess, "M05").WithArguments("I1.M05()").WithLocation(38, 33), - // (40,89): error CS9108: An expression tree may not contain an access of static abstract interface member + // (40,89): error CS8927: An expression tree may not contain an access of static abstract interface member // _ = (System.Linq.Expressions.Expression)(() => new System.Action(T.M01).ToString()); Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, "T.M01").WithLocation(40, 89) ); @@ -12471,7 +12471,7 @@ static void M02() where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (6,31): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,31): error CS8919: Target runtime doesn't support static abstract members in interfaces. // _ = new System.Action(T.M01); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "T.M01").WithLocation(6, 31) ); @@ -12481,7 +12481,7 @@ static void M02() where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (12,26): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (12,26): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static void M01(); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(12, 26) ); @@ -12588,7 +12588,7 @@ static void MT2() where T : I1 targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (8,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (8,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = (delegate*)&M01; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "(delegate*)&M01").WithLocation(8, 13), // (14,13): error CS8757: No overload for 'M01' matches function pointer 'delegate*' @@ -12597,7 +12597,7 @@ static void MT2() where T : I1 // (15,13): error CS8757: No overload for 'M04' matches function pointer 'delegate*' // _ = (delegate*)&this.M04; Diagnostic(ErrorCode.ERR_MethFuncPtrMismatch, "(delegate*)&this.M04").WithArguments("M04", "delegate*").WithLocation(15, 13), - // (27,13): error CS9107: A static abstract interface member can be accessed only on a type parameter. + // (27,13): error CS8926: A static abstract interface member can be accessed only on a type parameter. // _ = (delegate*)&I1.M01; Diagnostic(ErrorCode.ERR_BadAbstractStaticMemberAccess, "(delegate*)&I1.M01").WithLocation(27, 13), // (28,13): error CS8757: No overload for 'M01' matches function pointer 'delegate*' @@ -12730,7 +12730,7 @@ static void M02() where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (6,13): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,13): error CS8919: Target runtime doesn't support static abstract members in interfaces. // _ = (delegate*)&T.M01; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "(delegate*)&T.M01").WithLocation(6, 13) ); @@ -12740,7 +12740,7 @@ static void M02() where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (12,26): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (12,26): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static void M01(); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(12, 26) ); @@ -12851,7 +12851,7 @@ void I1.M01() {} // (8,10): error CS0535: 'C1' does not implement interface member 'I1.M01()' // C1 : I1 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01()").WithLocation(8, 10), - // (12,10): error CS9109: 'C2' does not implement static interface member 'I1.M01()'. 'C2.M01()' cannot implement the interface member because it is not static. + // (12,10): error CS8928: 'C2' does not implement static interface member 'I1.M01()'. 'C2.M01()' cannot implement the interface member because it is not static. // C2 : I1 Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotStatic, "I1").WithArguments("C2", "I1.M01()", "C2.M01()").WithLocation(12, 10), // (18,10): error CS0737: 'C3' does not implement interface member 'I1.M01()'. 'C3.M01()' cannot implement an interface member because it is not public. @@ -13125,7 +13125,7 @@ public static void M01() {} references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (2,12): error CS9110: 'Test1.M01()' cannot implement interface member 'I1.M01()' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.M01()' cannot implement interface member 'I1.M01()' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01()", "I1.M01()", "Test1").WithLocation(2, 12) ); @@ -13135,10 +13135,10 @@ public static void M01() {} targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (2,12): error CS9110: 'Test1.M01()' cannot implement interface member 'I1.M01()' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.M01()' cannot implement interface member 'I1.M01()' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01()", "I1.M01()", "Test1").WithLocation(2, 12), - // (9,26): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (9,26): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static void M01(); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(9, 26) ); @@ -13174,7 +13174,7 @@ static void I1.M01() {} references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (4,20): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (4,20): error CS8919: Target runtime doesn't support static abstract members in interfaces. // static void I1.M01() {} Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(4, 20) ); @@ -13184,10 +13184,10 @@ static void I1.M01() {} targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (4,20): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (4,20): error CS8919: Target runtime doesn't support static abstract members in interfaces. // static void I1.M01() {} Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(4, 20), - // (9,26): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (9,26): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static void M01(); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(9, 26) ); @@ -14521,7 +14521,7 @@ public interface I2 where T : I2 // (8,10): error CS0535: 'C1' does not implement interface member 'I1.operator +(C1)' // C1 : I1 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.operator " + op + "(C1)").WithLocation(8, 10), - // (12,10): error CS9109: 'C2' does not implement static interface member 'I1.operator +(C2)'. 'C2.operator +(C2)' cannot implement the interface member because it is not static. + // (12,10): error CS8928: 'C2' does not implement static interface member 'I1.operator +(C2)'. 'C2.operator +(C2)' cannot implement the interface member because it is not static. // C2 : I1 Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotStatic, "I1").WithArguments("C2", "I1.operator " + op + "(C2)", "C2.operator " + op + "(C2)").WithLocation(12, 10), // (14,24): error CS0558: User-defined operator 'C2.operator +(C2)' must be declared static and public @@ -14536,7 +14536,7 @@ public interface I2 where T : I2 // (24,10): error CS0535: 'C4' does not implement interface member 'I1.operator +(C4)' // C4 : I1 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C4", "I1.operator " + op + "(C4)").WithLocation(24, 10), - // (26,24): error CS9111: Explicit implementation of a user-defined operator 'C4.operator +(C4)' must be declared static + // (26,24): error CS8930: Explicit implementation of a user-defined operator 'C4.operator +(C4)' must be declared static // C4 I1.operator +(C4 x) => throw null; Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, op).WithArguments("C4.operator " + op + "(C4)").WithLocation(26, 24), // (26,24): error CS0539: 'C4.operator +(C4)' in explicit interface declaration is not found among members of the interface that can be implemented @@ -14659,7 +14659,7 @@ public interface I2 where T : I2 // (8,10): error CS0535: 'C1' does not implement interface member 'I1.operator >>(C1, int)' // C1 : I1 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.operator " + op + "(C1, int)").WithLocation(8, 10), - // (12,10): error CS9109: 'C2' does not implement static interface member 'I1.operator >>(C2, int)'. 'C2.operator >>(C2, int)' cannot implement the interface member because it is not static. + // (12,10): error CS8928: 'C2' does not implement static interface member 'I1.operator >>(C2, int)'. 'C2.operator >>(C2, int)' cannot implement the interface member because it is not static. // C2 : I1 Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotStatic, "I1").WithArguments("C2", "I1.operator " + op + "(C2, int)", "C2.operator " + op + "(C2, int)").WithLocation(12, 10), // (14,24): error CS0558: User-defined operator 'C2.operator >>(C2, int)' must be declared static and public @@ -14674,7 +14674,7 @@ public interface I2 where T : I2 // (24,10): error CS0535: 'C4' does not implement interface member 'I1.operator >>(C4, int)' // C4 : I1 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C4", "I1.operator " + op + "(C4, int)").WithLocation(24, 10), - // (26,24): error CS9111: Explicit implementation of a user-defined operator 'C4.operator >>(C4, int)' must be declared static + // (26,24): error CS8930: Explicit implementation of a user-defined operator 'C4.operator >>(C4, int)' must be declared static // C4 I1.operator >>(C4 x, int y) => throw null; Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, op).WithArguments("C4.operator " + op + "(C4, int)").WithLocation(26, 24), // (26,24): error CS0539: 'C4.operator >>(C4, int)' in explicit interface declaration is not found among members of the interface that can be implemented @@ -14802,7 +14802,7 @@ interface I14 : I1 // (17,24): error CS0562: The parameter of a unary operator must be the containing type // static I1 operator +(I1 x) => default; Diagnostic(badSignatureError, op).WithLocation(17, 24), - // (22,20): error CS9111: Explicit implementation of a user-defined operator 'I5.operator +(I1)' must be declared static + // (22,20): error CS8930: Explicit implementation of a user-defined operator 'I5.operator +(I1)' must be declared static // I1 I1.operator +(I1 x) => default; Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, op).WithArguments("I5.operator " + op + "(I1)").WithLocation(22, 20), // (22,20): error CS0539: 'I5.operator +(I1)' in explicit interface declaration is not found among members of the interface that can be implemented @@ -14811,7 +14811,7 @@ interface I14 : I1 // (27,27): error CS0539: 'I6.operator +(I1)' in explicit interface declaration is not found among members of the interface that can be implemented // static I1 I1.operator +(I1 x) => default; Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("I6.operator " + op + "(I1)").WithLocation(27, 27), - // (32,33): error CS9102: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. + // (32,33): error CS8921: The parameter of a unary operator must be the containing type, or its type parameter constrained to it. // abstract static I1 operator +(I1 x); Diagnostic(badAbstractSignatureError, op).WithLocation(32, 33), // (42,16): error CS0558: User-defined operator 'I8.operator +(T)' must be declared static and public @@ -14963,7 +14963,7 @@ interface I14 : I1 // (17,24): error CS0563: One of the parameters of a binary operator must be the containing type // static I1 operator |(I1 x, int y) => default; Diagnostic(badSignatureError, op).WithLocation(17, 24), - // (22,20): error CS9111: Explicit implementation of a user-defined operator 'I5.operator |(I1, int)' must be declared static + // (22,20): error CS8930: Explicit implementation of a user-defined operator 'I5.operator |(I1, int)' must be declared static // I1 I1.operator |(I1 x, int y) => default; Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, op).WithArguments("I5.operator " + op + "(I1, int)").WithLocation(22, 20), // (22,20): error CS0539: 'I5.operator |(I1, int)' in explicit interface declaration is not found among members of the interface that can be implemented @@ -14972,7 +14972,7 @@ interface I14 : I1 // (27,27): error CS0539: 'I6.operator |(I1, int)' in explicit interface declaration is not found among members of the interface that can be implemented // static I1 I1.operator |(I1 x, int y) => default; Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("I6.operator " + op + "(I1, int)").WithLocation(27, 27), - // (32,33): error CS9105: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. + // (32,33): error CS8924: One of the parameters of a binary operator must be the containing type, or its type parameter constrained to it. // abstract static I1 operator |(I1 x, int y); Diagnostic(badAbstractSignatureError, op).WithLocation(32, 33), // (42,16): error CS0563: One of the parameters of a binary operator must be the containing type @@ -15220,7 +15220,7 @@ public interface I1 where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_OperatorNeedsMatch or (int)ErrorCode.ERR_OpTFRetType)).Verify( - // (2,12): error CS9110: 'Test1.operator +(Test1)' cannot implement interface member 'I1.operator +(Test1)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.operator +(Test1)' cannot implement interface member 'I1.operator +(Test1)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.operator " + op + "(Test1)", "I1.operator " + op + "(Test1)", "Test1").WithLocation(2, 12) ); @@ -15230,10 +15230,10 @@ public interface I1 where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_OperatorNeedsMatch or (int)ErrorCode.ERR_OpTFRetType)).Verify( - // (2,12): error CS9110: 'Test1.operator +(Test1)' cannot implement interface member 'I1.operator +(Test1)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.operator +(Test1)' cannot implement interface member 'I1.operator +(Test1)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.operator " + op + "(Test1)", "I1.operator " + op + "(Test1)", "Test1").WithLocation(2, 12), - // (9,32): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (9,32): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static T operator +(T x); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(9, 32) ); @@ -15269,7 +15269,7 @@ public interface I1 where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_OperatorNeedsMatch or (int)ErrorCode.WRN_EqualityOpWithoutEquals or (int)ErrorCode.WRN_EqualityOpWithoutGetHashCode)).Verify( - // (2,12): error CS9110: 'Test1.operator >>(Test1, int)' cannot implement interface member 'I1.operator >>(Test1, int)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.operator >>(Test1, int)' cannot implement interface member 'I1.operator >>(Test1, int)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.operator " + op + "(Test1, int)", "I1.operator " + op + "(Test1, int)", "Test1").WithLocation(2, 12) ); @@ -15279,10 +15279,10 @@ public interface I1 where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_OperatorNeedsMatch or (int)ErrorCode.WRN_EqualityOpWithoutEquals or (int)ErrorCode.WRN_EqualityOpWithoutGetHashCode)).Verify( - // (2,12): error CS9110: 'Test1.operator >>(Test1, int)' cannot implement interface member 'I1.operator >>(Test1, int)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.operator >>(Test1, int)' cannot implement interface member 'I1.operator >>(Test1, int)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.operator " + op + "(Test1, int)", "I1.operator " + op + "(Test1, int)", "Test1").WithLocation(2, 12), - // (9,32): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (9,32): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static T operator >>(T x, int y); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(9, 32) ); @@ -15318,7 +15318,7 @@ public interface I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (4,27): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (4,27): error CS8919: Target runtime doesn't support static abstract members in interfaces. // static I1 I1.operator +(I1 x) => default; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(4, 27) ); @@ -15328,10 +15328,10 @@ public interface I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.GetDiagnostics().Where(d => d.Code is not ((int)ErrorCode.ERR_OperatorNeedsMatch or (int)ErrorCode.ERR_OpTFRetType)).Verify( - // (4,27): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (4,27): error CS8919: Target runtime doesn't support static abstract members in interfaces. // static I1 I1.operator +(I1 x) => default; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(4, 27), - // (9,33): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (9,33): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static I1 operator +(I1 x); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(9, 33) ); @@ -15367,7 +15367,7 @@ public interface I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (4,27): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (4,27): error CS8919: Target runtime doesn't support static abstract members in interfaces. // static I1 I1.operator +(I1 x, int y) => default; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(4, 27) ); @@ -15377,10 +15377,10 @@ public interface I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( - // (4,27): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (4,27): error CS8919: Target runtime doesn't support static abstract members in interfaces. // static I1 I1.operator +(I1 x, int y) => default; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(4, 27), - // (9,33): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (9,33): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static I1 operator +(I1 x, int y); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, op).WithLocation(9, 33) ); @@ -18148,7 +18148,7 @@ public interface I1 // (8,10): error CS0535: 'C1' does not implement interface member 'I1.M01' // C1 : I1 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01").WithLocation(8, 10), - // (12,10): error CS9109: 'C2' does not implement static interface member 'I1.M01'. 'C2.M01' cannot implement the interface member because it is not static. + // (12,10): error CS8928: 'C2' does not implement static interface member 'I1.M01'. 'C2.M01' cannot implement the interface member because it is not static. // C2 : I1 Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotStatic, "I1").WithArguments("C2", "I1.M01", "C2.M01").WithLocation(12, 10), // (18,10): error CS0737: 'C3' does not implement interface member 'I1.M01'. 'C3.M01' cannot implement an interface member because it is not public. @@ -18423,13 +18423,13 @@ public interface I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (2,12): error CS9110: 'Test1.M01.set' cannot implement interface member 'I1.M01.set' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.M01.set' cannot implement interface member 'I1.M01.set' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01.set", "I1.M01.set", "Test1").WithLocation(2, 12), - // (2,12): error CS9110: 'Test1.M01.get' cannot implement interface member 'I1.M01.get' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.M01.get' cannot implement interface member 'I1.M01.get' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01.get", "I1.M01.get", "Test1").WithLocation(2, 12), - // (2,12): error CS9110: 'Test1.M01' cannot implement interface member 'I1.M01' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.M01' cannot implement interface member 'I1.M01' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01", "I1.M01", "Test1").WithLocation(2, 12) ); @@ -18439,19 +18439,19 @@ public interface I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (2,12): error CS9110: 'Test1.M01.set' cannot implement interface member 'I1.M01.set' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.M01.set' cannot implement interface member 'I1.M01.set' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01.set", "I1.M01.set", "Test1").WithLocation(2, 12), - // (2,12): error CS9110: 'Test1.M01.get' cannot implement interface member 'I1.M01.get' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.M01.get' cannot implement interface member 'I1.M01.get' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01.get", "I1.M01.get", "Test1").WithLocation(2, 12), - // (2,12): error CS9110: 'Test1.M01' cannot implement interface member 'I1.M01' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.M01' cannot implement interface member 'I1.M01' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01", "I1.M01", "Test1").WithLocation(2, 12), - // (9,31): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (9,31): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static int M01 { get; set; } Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "get").WithLocation(9, 31), - // (9,36): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (9,36): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static int M01 { get; set; } Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "set").WithLocation(9, 36) ); @@ -18487,7 +18487,7 @@ public interface I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (4,19): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (4,19): error CS8919: Target runtime doesn't support static abstract members in interfaces. // static int I1.M01 { get; set; } Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(4, 19) ); @@ -18497,13 +18497,13 @@ public interface I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (4,19): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (4,19): error CS8919: Target runtime doesn't support static abstract members in interfaces. // static int I1.M01 { get; set; } Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(4, 19), - // (9,31): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (9,31): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static int M01 { get; set; } Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "get").WithLocation(9, 31), - // (9,36): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (9,36): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static int M01 { get; set; } Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "set").WithLocation(9, 36) ); @@ -20702,7 +20702,7 @@ static event System.Action I1.M01 { add{} remove{}} // (8,10): error CS0535: 'C1' does not implement interface member 'I1.M01' // C1 : I1 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1.M01").WithLocation(8, 10), - // (12,10): error CS9109: 'C2' does not implement static interface member 'I1.M01'. 'C2.M01' cannot implement the interface member because it is not static. + // (12,10): error CS8928: 'C2' does not implement static interface member 'I1.M01'. 'C2.M01' cannot implement the interface member because it is not static. // C2 : I1 Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotStatic, "I1").WithArguments("C2", "I1.M01", "C2.M01").WithLocation(12, 10), // (18,10): error CS0737: 'C3' does not implement interface member 'I1.M01'. 'C3.M01' cannot implement an interface member because it is not public. @@ -20983,13 +20983,13 @@ public static event System.Action M01 { add{} remove{} } references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (2,12): error CS9110: 'Test1.M01.remove' cannot implement interface member 'I1.M01.remove' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.M01.remove' cannot implement interface member 'I1.M01.remove' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01.remove", "I1.M01.remove", "Test1").WithLocation(2, 12), - // (2,12): error CS9110: 'Test1.M01.add' cannot implement interface member 'I1.M01.add' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.M01.add' cannot implement interface member 'I1.M01.add' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01.add", "I1.M01.add", "Test1").WithLocation(2, 12), - // (2,12): error CS9110: 'Test1.M01' cannot implement interface member 'I1.M01' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.M01' cannot implement interface member 'I1.M01' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01", "I1.M01", "Test1").WithLocation(2, 12) ); @@ -20999,16 +20999,16 @@ public static event System.Action M01 { add{} remove{} } targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (2,12): error CS9110: 'Test1.M01.remove' cannot implement interface member 'I1.M01.remove' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.M01.remove' cannot implement interface member 'I1.M01.remove' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01.remove", "I1.M01.remove", "Test1").WithLocation(2, 12), - // (2,12): error CS9110: 'Test1.M01.add' cannot implement interface member 'I1.M01.add' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.M01.add' cannot implement interface member 'I1.M01.add' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01.add", "I1.M01.add", "Test1").WithLocation(2, 12), - // (2,12): error CS9110: 'Test1.M01' cannot implement interface member 'I1.M01' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.M01' cannot implement interface member 'I1.M01' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1.M01", "I1.M01", "Test1").WithLocation(2, 12), - // (9,41): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (9,41): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static event System.Action M01; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(9, 41) ); @@ -21044,7 +21044,7 @@ public interface I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (4,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (4,35): error CS8919: Target runtime doesn't support static abstract members in interfaces. // static event System.Action I1.M01 { add => throw null; remove => throw null; } Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(4, 35) ); @@ -21054,10 +21054,10 @@ public interface I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (4,35): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (4,35): error CS8919: Target runtime doesn't support static abstract members in interfaces. // static event System.Action I1.M01 { add => throw null; remove => throw null; } Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(4, 35), - // (9,41): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (9,41): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static event System.Action M01; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "M01").WithLocation(9, 41) ); @@ -23242,7 +23242,7 @@ public interface I2 where T : I2 // (8,10): error CS0535: 'C1' does not implement interface member 'I1.explicit operator int(C1)' // C1 : I1 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C1", "I1." + op + " operator int(C1)").WithLocation(8, 10), - // (12,10): error CS9109: 'C2' does not implement static interface member 'I1.explicit operator int(C2)'. 'C2.explicit operator int(C2)' cannot implement the interface member because it is not static. + // (12,10): error CS8928: 'C2' does not implement static interface member 'I1.explicit operator int(C2)'. 'C2.explicit operator int(C2)' cannot implement the interface member because it is not static. // C2 : I1 Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberNotStatic, "I1").WithArguments("C2", "I1." + op + " operator int(C2)", "C2." + op + " operator int(C2)").WithLocation(12, 10), // (14,30): error CS0558: User-defined operator 'C2.explicit operator int(C2)' must be declared static and public @@ -23257,7 +23257,7 @@ public interface I2 where T : I2 // (24,10): error CS0535: 'C4' does not implement interface member 'I1.explicit operator int(C4)' // C4 : I1 Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C4", "I1." + op + " operator int(C4)").WithLocation(24, 10), - // (26,30): error CS9111: Explicit implementation of a user-defined operator 'C4.explicit operator int(C4)' must be declared static + // (26,30): error CS8930: Explicit implementation of a user-defined operator 'C4.explicit operator int(C4)' must be declared static // explicit I1.operator int(C4 x) => throw null; Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C4." + op + " operator int(C4)").WithLocation(26, 30), // (26,30): error CS0539: 'C4.explicit operator int(C4)' in explicit interface declaration is not found among members of the interface that can be implemented @@ -23385,7 +23385,7 @@ interface I14 : I1 where T : I1 // (17,30): error CS0567: Conversion, equality, or inequality operators declared in interfaces must be abstract // static implicit operator int(T x) => default; Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "int").WithLocation(17, 30), - // (22,29): error CS9111: Explicit implementation of a user-defined operator 'I5.implicit operator int(T)' must be declared static + // (22,29): error CS8930: Explicit implementation of a user-defined operator 'I5.implicit operator int(T)' must be declared static // implicit I1.operator int(T x) => default; Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("I5." + op + " operator int(T)").WithLocation(22, 29), // (22,29): error CS0539: 'I5.implicit operator int(T)' in explicit interface declaration is not found among members of the interface that can be implemented @@ -23394,7 +23394,7 @@ interface I14 : I1 where T : I1 // (27,36): error CS0539: 'I6.implicit operator int(T)' in explicit interface declaration is not found among members of the interface that can be implemented // static implicit I1.operator int(T x) => default; Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "int").WithArguments("I6." + op + " operator int(T)").WithLocation(27, 36), - // (32,39): error CS9112: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + // (32,39): error CS8931: User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type // abstract static implicit operator int(T x); Diagnostic(ErrorCode.ERR_AbstractConversionNotInvolvingContainedType, "int").WithLocation(32, 39), // (42,23): error CS0556: User-defined conversion must convert to or from the enclosing type @@ -23543,7 +23543,7 @@ public interface I1 where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (2,12): error CS9110: 'Test1.explicit operator int(Test1)' cannot implement interface member 'I1.explicit operator int(Test1)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.explicit operator int(Test1)' cannot implement interface member 'I1.explicit operator int(Test1)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1." + op + " operator int(Test1)", "I1." + op + " operator int(Test1)", "Test1").WithLocation(2, 12) ); @@ -23553,10 +23553,10 @@ public interface I1 where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (2,12): error CS9110: 'Test1.explicit operator int(Test1)' cannot implement interface member 'I1.explicit operator int(Test1)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. + // (2,12): error CS8929: 'Test1.explicit operator int(Test1)' cannot implement interface member 'I1.explicit operator int(Test1)' in type 'Test1' because the target runtime doesn't support static abstract members in interfaces. // Test1: I1 Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember, "I1").WithArguments("Test1." + op + " operator int(Test1)", "I1." + op + " operator int(Test1)", "Test1").WithLocation(2, 12), - // (9,39): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (9,39): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static explicit operator int(T x); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "int").WithLocation(9, 39) ); @@ -23592,7 +23592,7 @@ public interface I1 where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (4,40): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (4,40): error CS8919: Target runtime doesn't support static abstract members in interfaces. // static explicit I1.operator int(Test1 x) => default; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "int").WithLocation(4, 40) ); @@ -23602,10 +23602,10 @@ public interface I1 where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (4,40): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (4,40): error CS8919: Target runtime doesn't support static abstract members in interfaces. // static explicit I1.operator int(Test1 x) => default; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "int").WithLocation(4, 40), - // (9,39): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (9,39): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static explicit operator int(T x); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "int").WithLocation(9, 39) ); @@ -24803,7 +24803,7 @@ static void MT2() // (21,16): error CS0030: Cannot convert type 'I1' to 'int' // return (int)a; Diagnostic(error, cast + "a").WithArguments("I1", "int").WithLocation(21, 16), - // (26,80): error CS9108: An expression tree may not contain an access of static abstract interface member + // (26,80): error CS8927: An expression tree may not contain an access of static abstract interface member // _ = (System.Linq.Expressions.Expression>)((T b) => (int)b); Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, cast + "b").WithLocation(26, 80) ); @@ -25307,7 +25307,7 @@ static int M02(T x) where T : I1 references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (6,16): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,16): error CS8919: Target runtime doesn't support static abstract members in interfaces. // return (int)x; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, (needCast ? "(int)" : "") + "x").WithLocation(6, 16) ); @@ -25317,7 +25317,7 @@ static int M02(T x) where T : I1 targetFramework: TargetFramework.DesktopLatestExtended); compilation3.GetDiagnostics().Where(d => d.Code is not (int)ErrorCode.ERR_OperatorNeedsMatch).Verify( - // (12,39): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (12,39): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static explicit operator int(T x); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "int").WithLocation(12, 39) ); @@ -25363,7 +25363,7 @@ class C references: new[] { compilation1.ToMetadataReference() }); compilation2.VerifyDiagnostics( - // (6,13): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (6,13): error CS8919: Target runtime doesn't support static abstract members in interfaces. // _ = x == x; Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "x " + op + " x").WithLocation(6, 13) ); @@ -25373,7 +25373,7 @@ class C targetFramework: TargetFramework.DesktopLatestExtended); compilation3.VerifyDiagnostics( - // (21,39): error CS9100: Target runtime doesn't support static abstract members in interfaces. + // (21,39): error CS8919: Target runtime doesn't support static abstract members in interfaces. // abstract static implicit operator bool(T x); Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, "bool").WithLocation(21, 39) ); From e16ad8f14e033c7eb4a85694f7a79dab4ead7a8a Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Tue, 15 Jun 2021 10:58:18 -0700 Subject: [PATCH 120/127] Address feedback --- .../InheritanceMarginServiceHelpers.cs | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs b/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs index 014b1e151591a..875b49ff4f9bb 100644 --- a/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs +++ b/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginServiceHelpers.cs @@ -230,8 +230,7 @@ private static async ValueTask CreateInherita var derivedTypeItems = await derivedTypesSymbols .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() - .SelectAsArrayAsync((symbol, _) => - CreateInheritanceItemAsync(solution, + .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync(solution, symbol, InheritanceRelationship.ImplementingType, cancellationToken), cancellationToken) @@ -280,23 +279,16 @@ private static async ValueTask CreateInherita var baseSymbolItems = await baseSymbols .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() - .SelectAsArrayAsync((symbol, _) => - symbol.IsInterfaceType() - ? CreateInheritanceItemAsync( - solution, - symbol, - InheritanceRelationship.ImplementedInterface, cancellationToken) - : CreateInheritanceItemAsync( - solution, - symbol, - InheritanceRelationship.BaseType, cancellationToken), cancellationToken) - .ConfigureAwait(false); + .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync( + solution, + symbol, + symbol.IsInterfaceType() ? InheritanceRelationship.ImplementedInterface : InheritanceRelationship.BaseType, + cancellationToken), cancellationToken).ConfigureAwait(false); var derivedTypeItems = await derivedTypesSymbols .SelectAsArray(symbol => symbol.OriginalDefinition) .Distinct() - .SelectAsArrayAsync((symbol, _) => - CreateInheritanceItemAsync(solution, + .SelectAsArrayAsync((symbol, _) => CreateInheritanceItemAsync(solution, symbol, InheritanceRelationship.DerivedType, cancellationToken), cancellationToken) @@ -349,9 +341,7 @@ private static async ValueTask CreateInherita lineNumber, FindUsagesHelpers.GetDisplayParts(memberSymbol), memberSymbol.GetGlyph(), - implementedMemberItems - .Concat(overridenMemberItems) - .Concat(overridingMemberItems)); + implementedMemberItems.Concat(overridenMemberItems).Concat(overridingMemberItems)); } private static async ValueTask CreateInheritanceItemAsync( From 0e720cecad0145d63620a65011f18509052e799b Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 15 Jun 2021 11:23:20 -0700 Subject: [PATCH 121/127] Mark "record structs" and "global usings" as done (#54093) --- docs/Language Feature Status.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Language Feature Status.md b/docs/Language Feature Status.md index 5a28acb0fe789..9ceeaa6f364df 100644 --- a/docs/Language Feature Status.md +++ b/docs/Language Feature Status.md @@ -10,8 +10,8 @@ efforts behind them. | Feature | Branch | State | Developer | Reviewer | LDM Champ | | ------- | ------ | ----- | --------- | -------- | --------- | -| [Record structs](https://github.com/dotnet/csharplang/issues/4334) | [record-structs](https://github.com/dotnet/roslyn/tree/features/record-structs) | [In Progress](https://github.com/dotnet/roslyn/issues/51199) | [jcouv](https://github.com/jcouv) | [AlekseyTs](https://github.com/AlekseyTs), [RikkiGibson](https://github.com/RikkiGibson) | [jcouv](https://github.com/jcouv) | -| [Global Using Directive](https://github.com/dotnet/csharplang/issues/3428) | [GlobalUsingDirective](https://github.com/dotnet/roslyn/tree/features/GlobalUsingDirective) | [In Progress](https://github.com/dotnet/roslyn/issues/51307) | [AlekseyTs](https://github.com/AlekseyTs) | [333fred](https://github.com/333fred), [cston](https://github.com/cston) | [AlekseyTs](https://github.com/AlekseyTs) | +| [Record structs](https://github.com/dotnet/csharplang/issues/4334) | [record-structs](https://github.com/dotnet/roslyn/tree/features/record-structs) | [Merged into 16.11](https://github.com/dotnet/roslyn/issues/51199) | [jcouv](https://github.com/jcouv) | [AlekseyTs](https://github.com/AlekseyTs), [RikkiGibson](https://github.com/RikkiGibson) | [jcouv](https://github.com/jcouv) | +| [Global Using Directive](https://github.com/dotnet/csharplang/issues/3428) | [GlobalUsingDirective](https://github.com/dotnet/roslyn/tree/features/GlobalUsingDirective) | [Merged into 16.11](https://github.com/dotnet/roslyn/issues/51307) | [AlekseyTs](https://github.com/AlekseyTs) | [333fred](https://github.com/333fred), [cston](https://github.com/cston) | [AlekseyTs](https://github.com/AlekseyTs) | | [Static Abstract Members In Interfaces](https://github.com/dotnet/csharplang/issues/4436) | [StaticAbstractMembersInInterfaces](https://github.com/dotnet/roslyn/tree/features/StaticAbstractMembersInInterfaces) | [In Progress](https://github.com/dotnet/roslyn/issues/52221) | [AlekseyTs](https://github.com/AlekseyTs) | [333fred](https://github.com/333fred), [RikkiGibson](https://github.com/RikkiGibson) | [MadsTorgersen](https://github.com/MadsTorgersen) | | [File-scoped namespace](https://github.com/dotnet/csharplang/issues/137) | [FileScopedNamespaces](https://github.com/dotnet/roslyn/tree/features/FileScopedNamespaces) | [In Progress](https://github.com/dotnet/roslyn/issues/49000) | [RikkiGibson](https://github.com/RikkiGibson) | [jcouv](https://github.com/jcouv), [chsienki](https://github.com/chsienki) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | | [Interpolated string improvements](https://github.com/dotnet/csharplang/issues/4487) | [interpolated-string](https://github.com/dotnet/roslyn/tree/features/interpolated-string) | [In Progress](https://github.com/dotnet/roslyn/issues/51499) | [333fred](https://github.com/333fred) | [AlekseyTs](https://github.com/AlekseyTs), [chsienki](https://github.com/chsienki) | [jaredpar](https://github.com/jaredpar) | From dfd81854ba2a131e4abb4d394137769270aa9040 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Tue, 15 Jun 2021 11:32:26 -0700 Subject: [PATCH 122/127] Add Quick Info tests for static abstract inheritdoc --- .../QuickInfo/SemanticQuickInfoSourceTests.cs | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index 4973fdf7d92fe..ab87495bb8216 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -7304,5 +7304,144 @@ void M2() }", MainDescription($"({FeaturesResources.local_variable}) string? x")); } + + [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] + public async Task TestStaticAbstract_ImplicitImplementation() + { + var code = @" +interface I1 +{ + /// Summary text + static abstract void M1(); +} + +class C1_1 : I1 +{ + public static void $$M1() { } +} +"; + + await TestAsync( + code, + MainDescription("void C1_1.M1()"), + Documentation("Summary text")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] + public async Task TestStaticAbstract_ImplicitImplementation_FromReference() + { + var code = @" +interface I1 +{ + /// Summary text + static abstract void M1(); +} + +class C1_1 : I1 +{ + public static void M1() { } +} + +class R +{ + public static void M() { C1_1.$$M1(); } +} +"; + + await TestAsync( + code, + MainDescription("void C1_1.M1()"), + Documentation("Summary text")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] + public async Task TestStaticAbstract_FromTypeParameterReference() + { + var code = @" +interface I1 +{ + /// Summary text + static abstract void M1(); +} + +class R +{ + public static void M() where T : I1 { T.$$M1(); } +} +"; + + await TestAsync( + code, + MainDescription("void I1.M1()"), + Documentation("Summary text")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] + public async Task TestStaticAbstract_ExplicitInheritdoc_ImplicitImplementation() + { + var code = @" +interface I1 +{ + /// Summary text + static abstract void M1(); +} + +class C1_1 : I1 +{ + /// + public static void $$M1() { } +} +"; + + await TestAsync( + code, + MainDescription("void C1_1.M1()"), + Documentation("Summary text")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] + public async Task TestStaticAbstract_ExplicitImplementation() + { + var code = @" +interface I1 +{ + /// Summary text + static abstract void M1(); +} + +class C1_1 : I1 +{ + static void I1.$$M1() { } +} +"; + + await TestAsync( + code, + MainDescription("void C1_1.M1()"), + Documentation("Summary text")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] + public async Task TestStaticAbstract_ExplicitInheritdoc_ExplicitImplementation() + { + var code = @" +interface I1 +{ + /// Summary text + static abstract void M1(); +} + +class C1_1 : I1 +{ + /// + static void I1.$$M1() { } +} +"; + + await TestAsync( + code, + MainDescription("void C1_1.M1()"), + Documentation("Summary text")); + } } } From 2f3253e215c7b7c761b0ecef06493a719b490c56 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Tue, 15 Jun 2021 12:54:41 -0700 Subject: [PATCH 123/127] Disallow UnmanagedCallersOnly for abstract static methods. --- .../CSharp/Portable/CSharpResources.resx | 6 +- .../CSharp/Portable/Errors/ErrorCode.cs | 1 + .../CSharp/Portable/Symbols/MethodSymbol.cs | 2 +- .../CSharp/Portable/Symbols/TypeSymbol.cs | 4 + .../Portable/xlf/CSharpResources.cs.xlf | 9 +- .../Portable/xlf/CSharpResources.de.xlf | 9 +- .../Portable/xlf/CSharpResources.es.xlf | 9 +- .../Portable/xlf/CSharpResources.fr.xlf | 9 +- .../Portable/xlf/CSharpResources.it.xlf | 9 +- .../Portable/xlf/CSharpResources.ja.xlf | 9 +- .../Portable/xlf/CSharpResources.ko.xlf | 9 +- .../Portable/xlf/CSharpResources.pl.xlf | 9 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 9 +- .../Portable/xlf/CSharpResources.ru.xlf | 9 +- .../Portable/xlf/CSharpResources.tr.xlf | 9 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 9 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 9 +- .../CodeGen/CodeGenFunctionPointersTests.cs | 69 ++++- .../Test/Semantic/Semantics/LambdaTests.cs | 2 +- .../StaticAbstractMembersInInterfacesTests.cs | 260 ++++++++++++++++++ 20 files changed, 420 insertions(+), 41 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index daf3ab1ff2948..4bd0ba1c1f8a2 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6527,7 +6527,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ extension GetEnumerator - 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. + 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. UnmanagedCallersOnly is not localizable. @@ -6697,4 +6697,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ User-defined conversion in an interface must convert to or from a type parameter on the enclosing type constrained to the enclosing type + + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + UnmanagedCallersOnly is not localizable. + diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 25b933787246f..e03b44df81248 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1954,6 +1954,7 @@ internal enum ErrorCode ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfacesForMember = 8929, ERR_ExplicitImplementationOfOperatorsMustBeStatic = 8930, ERR_AbstractConversionNotInvolvingContainedType = 8931, + ERR_InterfaceImplementedByUnmanagedCallersOnlyMethod = 8932, #endregion diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs index 6c77da950b036..85030a8a2eddf 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs @@ -1016,7 +1016,7 @@ internal bool CheckAndReportValidUnmanagedCallersOnlyTarget(Location? location, { Debug.Assert((location == null) == (diagnostics == null)); - if (!IsStatic || MethodKind is not (MethodKind.Ordinary or MethodKind.LocalFunction)) + if (!IsStatic || IsAbstract || MethodKind is not (MethodKind.Ordinary or MethodKind.LocalFunction)) { // `UnmanagedCallersOnly` can only be applied to ordinary static methods or local functions. diagnostics?.Add(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, location!); diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs index c1b7025675bf4..06d225496c753 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs @@ -1661,6 +1661,10 @@ private static void ReportImplicitImplementationMatchDiagnostics(Symbol interfac // CS0629: Conditional member '{0}' cannot implement interface member '{1}' in type '{2}' diagnostics.Add(ErrorCode.ERR_InterfaceImplementedByConditional, GetImplicitImplementationDiagnosticLocation(interfaceMember, implementingType, implicitImpl), implicitImpl, interfaceMethod, implementingType); } + else if (implicitImplMethod.IsStatic && implicitImplMethod.MethodKind == MethodKind.Ordinary && implicitImplMethod.GetUnmanagedCallersOnlyAttributeData(forceComplete: true) is not null) + { + diagnostics.Add(ErrorCode.ERR_InterfaceImplementedByUnmanagedCallersOnlyMethod, GetImplicitImplementationDiagnosticLocation(interfaceMember, implementingType, implicitImpl), implicitImpl, interfaceMethod, implementingType); + } else if (ReportAnyMismatchedConstraints(interfaceMethod, implementingType, implicitImplMethod, diagnostics)) { reportedAnError = true; diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 2769b4373df84..b5ff532eba2e8 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -582,6 +582,11 @@ Vlastnosti instance v rozhraních nemůžou mít inicializátory. + + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + UnmanagedCallersOnly is not localizable. + '{0}' cannot implement interface member '{1}' in type '{2}' because it has an __arglist parameter {0} nemůže implementovat člen rozhraní {1} v typu {2}, protože má parametr __arglist. @@ -1243,8 +1248,8 @@ UnmanagedCallersOnly is not localizable. - 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. - UnmanagedCallersOnly se dá použít jen pro běžné statické metody nebo statické místní funkce. + 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + UnmanagedCallersOnly se dá použít jen pro běžné statické metody nebo statické místní funkce. UnmanagedCallersOnly is not localizable. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index e8b5bfa507263..07c6f14fe92b7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -582,6 +582,11 @@ Instanzeigenschaften in Schnittstellen können keine Initialisierer aufweisen. + + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + UnmanagedCallersOnly is not localizable. + '{0}' cannot implement interface member '{1}' in type '{2}' because it has an __arglist parameter "{0}" kann den Schnittstellenmember "{1}" in Typ "{2}" nicht implementieren, weil er einen __arglist-Parameter umfasst. @@ -1243,8 +1248,8 @@ UnmanagedCallersOnly is not localizable. - 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. - "UnmanagedCallersOnly" kann nur auf normale statische Methoden oder statische lokale Funktionen angewendet werden. + 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + "UnmanagedCallersOnly" kann nur auf normale statische Methoden oder statische lokale Funktionen angewendet werden. UnmanagedCallersOnly is not localizable. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 0007eadb0303d..24e859006a224 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -582,6 +582,11 @@ Las propiedades de la instancia en las interfaces no pueden tener inicializadores. + + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + UnmanagedCallersOnly is not localizable. + '{0}' cannot implement interface member '{1}' in type '{2}' because it has an __arglist parameter “{0}” no puede implementar el miembro de interfaz “{1}” en el tipo “{2}” porque tiene un parámetro __arglist @@ -1243,8 +1248,8 @@ UnmanagedCallersOnly is not localizable. - 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. - "UnmanagedCallersOnly" solo se puede aplicar a las funciones locales estáticas o los métodos estáticos ordinarios. + 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + "UnmanagedCallersOnly" solo se puede aplicar a las funciones locales estáticas o los métodos estáticos ordinarios. UnmanagedCallersOnly is not localizable. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 51d63313fd0d8..ab43b93d40b3e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -582,6 +582,11 @@ Les propriétés d'instance dans les interfaces ne peuvent pas avoir d'initialiseurs. + + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + UnmanagedCallersOnly is not localizable. + '{0}' cannot implement interface member '{1}' in type '{2}' because it has an __arglist parameter '{0}' ne peut pas implémenter le membre d'interface '{1}' dans le type '{2}', car il a un paramètre __arglist @@ -1243,8 +1248,8 @@ UnmanagedCallersOnly is not localizable. - 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. - 'UnmanagedCallersOnly' peut uniquement être appliqué aux méthodes statiques ordinaires ou aux fonctions locales statiques. + 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + 'UnmanagedCallersOnly' peut uniquement être appliqué aux méthodes statiques ordinaires ou aux fonctions locales statiques. UnmanagedCallersOnly is not localizable. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 7319d7426c5ee..609c78d59957e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -582,6 +582,11 @@ Le proprietà di istanza nelle interfacce non possono avere inizializzatori. + + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + UnmanagedCallersOnly is not localizable. + '{0}' cannot implement interface member '{1}' in type '{2}' because it has an __arglist parameter '{0}' non può implementare il membro di interfaccia '{1}' nel tipo '{2}' perché contiene un parametro __arglist @@ -1243,8 +1248,8 @@ UnmanagedCallersOnly is not localizable. - 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. - 'UnmanagedCallersOnly' può essere applicato solo a metodi statici normali o funzioni locali statiche. + 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + 'UnmanagedCallersOnly' può essere applicato solo a metodi statici normali o funzioni locali statiche. UnmanagedCallersOnly is not localizable. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 72d9039fc82bf..82fac7d26b455 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -582,6 +582,11 @@ インターフェイス内のインスタンス プロパティは初期化子を持つことができません。 + + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + UnmanagedCallersOnly is not localizable. + '{0}' cannot implement interface member '{1}' in type '{2}' because it has an __arglist parameter '{0}' は、__arglist パラメーターが指定されているため、型 '{2}' のインターフェイス メンバー '{1}' を実装できません @@ -1243,8 +1248,8 @@ UnmanagedCallersOnly is not localizable. - 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. - 'UnmanagedCallersOnly' は、通常の静的メソッドまたは静的ローカル関数にのみ適用できます。 + 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + 'UnmanagedCallersOnly' は、通常の静的メソッドまたは静的ローカル関数にのみ適用できます。 UnmanagedCallersOnly is not localizable. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index b584428eec4fa..f032a025fb533 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -582,6 +582,11 @@ 인터페이스의 인스턴스 속성은 이니셜라이저를 사용할 수 없습니다. + + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + UnmanagedCallersOnly is not localizable. + '{0}' cannot implement interface member '{1}' in type '{2}' because it has an __arglist parameter '{0}'은(는) __arglist 매개 변수가 있으므로 '{2}' 형식의 인터페이스 멤버 '{1}'을(를) 구현할 수 없습니다. @@ -1243,8 +1248,8 @@ UnmanagedCallersOnly is not localizable. - 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. - 'UnmanagedCallersOnly'는 일반 정적 메서드 또는 정적 로컬 함수에만 적용할 수 있습니다. + 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + 'UnmanagedCallersOnly'는 일반 정적 메서드 또는 정적 로컬 함수에만 적용할 수 있습니다. UnmanagedCallersOnly is not localizable. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index f1c928ff6a7ee..46547ef8a331d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -582,6 +582,11 @@ Właściwości wystąpienia w interfejsach nie mogą mieć inicjatorów. + + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + UnmanagedCallersOnly is not localizable. + '{0}' cannot implement interface member '{1}' in type '{2}' because it has an __arglist parameter Obiekt „{0}” nie może implementować elementu członkowskiego interfejsu „{1}” w ramach typu „{2}”, ponieważ zawiera on parametr __arglist @@ -1243,8 +1248,8 @@ UnmanagedCallersOnly is not localizable. - 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. - Atrybut „UnmanagedCallersOnly” może być stosowany tylko do zwykłych metod statycznych lub statycznych funkcji lokalnych. + 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + Atrybut „UnmanagedCallersOnly” może być stosowany tylko do zwykłych metod statycznych lub statycznych funkcji lokalnych. UnmanagedCallersOnly is not localizable. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 91685cb7dc2d3..4efe7d6a752f7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -582,6 +582,11 @@ As propriedades da instância nas interfaces não podem ter inicializadores. + + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + UnmanagedCallersOnly is not localizable. + '{0}' cannot implement interface member '{1}' in type '{2}' because it has an __arglist parameter '{0}' não pode implementar o membro de interface '{1}' no tipo '{2}' porque tem um parâmetro __arglist @@ -1243,8 +1248,8 @@ UnmanagedCallersOnly is not localizable. - 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. - 'UnmanagedCallersOnly' só pode ser aplicado a métodos estáticos comuns ou a funções locais estáticas. + 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + 'UnmanagedCallersOnly' só pode ser aplicado a métodos estáticos comuns ou a funções locais estáticas. UnmanagedCallersOnly is not localizable. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 1e5d05b3d3f07..0768c0734bd88 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -582,6 +582,11 @@ Свойства экземпляра в интерфейсах не могут иметь инициализаторы. + + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + UnmanagedCallersOnly is not localizable. + '{0}' cannot implement interface member '{1}' in type '{2}' because it has an __arglist parameter "{0}" не может реализовать член интерфейса "{1}" в типе "{2}" из-за наличия параметра __arglist @@ -1243,8 +1248,8 @@ UnmanagedCallersOnly is not localizable. - 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. - "UnmanagedCallersOnly" может применяться только к обычным статическим методам или к статическим локальным функциям. + 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + "UnmanagedCallersOnly" может применяться только к обычным статическим методам или к статическим локальным функциям. UnmanagedCallersOnly is not localizable. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 7eca34fca04f7..9e0da73b9a425 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -582,6 +582,11 @@ Arabirimlerdeki örnek özelliklerinin başlatıcıları olamaz. + + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + UnmanagedCallersOnly is not localizable. + '{0}' cannot implement interface member '{1}' in type '{2}' because it has an __arglist parameter '{0}' bir __arglist parametresine sahip olduğundan '{2}' türünde '{1}' arabirim üyesini uygulayamıyor. @@ -1243,8 +1248,8 @@ UnmanagedCallersOnly is not localizable. - 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. - 'UnmanagedCallersOnly', yalnızca normal statik metotlara veya statik yerel işlevlere uygulanabilir. + 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + 'UnmanagedCallersOnly', yalnızca normal statik metotlara veya statik yerel işlevlere uygulanabilir. UnmanagedCallersOnly is not localizable. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 92c5632d90a02..46b9cba19968d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -582,6 +582,11 @@ 接口中的实例属性不能具有初始值设定项。 + + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + UnmanagedCallersOnly is not localizable. + '{0}' cannot implement interface member '{1}' in type '{2}' because it has an __arglist parameter “{0}”无法在类型“{2}”中实现接口成员“{1}”,因为它具有 __arglist 参数 @@ -1243,8 +1248,8 @@ UnmanagedCallersOnly is not localizable. - 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. - "UnmanagedCallersOnly" 仅可应用于普通静态方法或静态本地函数。 + 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + "UnmanagedCallersOnly" 仅可应用于普通静态方法或静态本地函数。 UnmanagedCallersOnly is not localizable. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 256fe705523a5..fcef9719d84c0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -582,6 +582,11 @@ 介面中的執行個體屬性不可有初始設定式。 + + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + 'UnmanagedCallersOnly' method '{0}' cannot implement interface member '{1}' in type '{2}' + UnmanagedCallersOnly is not localizable. + '{0}' cannot implement interface member '{1}' in type '{2}' because it has an __arglist parameter 因為介面成員 '{1}' 包含 __arglist 參數,所以 '{0}' 無法在類型 '{2}' 中實作此介面成員 @@ -1243,8 +1248,8 @@ UnmanagedCallersOnly is not localizable. - 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. - 'UnmanagedCallersOnly' 僅適用於一般靜態方法或靜態區域函式。 + 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + 'UnmanagedCallersOnly' 僅適用於一般靜態方法或靜態區域函式。 UnmanagedCallersOnly is not localizable. diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs index 9e638053b093a..8433400cbb5d0 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs @@ -7787,10 +7787,10 @@ void local() {} ", UnmanagedCallersOnlyAttribute }); comp.VerifyDiagnostics( - // (6,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. + // (6,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. // [UnmanagedCallersOnly] Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(6, 6), - // (11,10): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. + // (11,10): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. // [UnmanagedCallersOnly] Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(11, 10) ); @@ -8687,6 +8687,51 @@ public static void M2(C c) ); } + [Fact] + [WorkItem(54113, "https://github.com/dotnet/roslyn/issues/54113")] + public void UnmanagedCallersOnlyDefinedOnConversion() + { + var il = UnmanagedCallersOnlyAttributeIl + @" +.class public auto ansi beforefieldinit C + extends [mscorlib]System.Object +{ + .method public hidebysig specialname static + int32 op_Implicit ( + class C i + ) cil managed + { + // [System.Runtime.InteropServices.UnmanagedCallersOnly] + .custom instance void System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) + + IL_0000: ldc.i4.0 + IL_0001: ret + } + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } +} +"; + var comp = CreateCompilationWithIL(@" +class Test +{ + void M(C x) + { + _ = (int)x; + } +} +", il); + + comp.VerifyDiagnostics(); + } + [Fact] public void UnmanagedCallersOnlyDefinedOnProperty_InSource() { @@ -8708,10 +8753,10 @@ static void M() ", UnmanagedCallersOnlyAttribute }); comp.VerifyDiagnostics( - // (7,10): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. + // (7,10): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. // [UnmanagedCallersOnly] get => throw null; Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(7, 10), - // (8,10): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. + // (8,10): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. // [UnmanagedCallersOnly] set => throw null; Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(8, 10) ); @@ -8793,7 +8838,7 @@ static void M() ", UnmanagedCallersOnlyAttribute }); comp.VerifyDiagnostics( - // (5,28): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. + // (5,28): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. // static int Prop { [UnmanagedCallersOnly] get {} } Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(5, 28) ); @@ -8933,10 +8978,10 @@ static void M(C c) ", UnmanagedCallersOnlyAttribute }); comp.VerifyDiagnostics( - // (7,10): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. + // (7,10): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. // [UnmanagedCallersOnly] set => throw null; Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(7, 10), - // (8,10): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. + // (8,10): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. // [UnmanagedCallersOnly] get => throw null; Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(8, 10) ); @@ -9014,7 +9059,7 @@ static void M(C c1, C c2) ", UnmanagedCallersOnlyAttribute }); comp.VerifyDiagnostics( - // (5,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. + // (5,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. // [UnmanagedCallersOnly] Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(5, 6) ); @@ -9076,7 +9121,7 @@ static void M(C c) ", UnmanagedCallersOnlyAttribute }); comp.VerifyDiagnostics( - // (5,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. + // (5,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. // [UnmanagedCallersOnly] Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(5, 6) ); @@ -9214,7 +9259,7 @@ public static class CExt ", UnmanagedCallersOnlyAttribute }); comp.VerifyDiagnostics( - // (12,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. + // (12,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. // [UnmanagedCallersOnly] Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(12, 6) ); @@ -9246,7 +9291,7 @@ public static class CExt ", UnmanagedCallersOnlyAttribute }); comp.VerifyDiagnostics( - // (14,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. + // (14,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. // [UnmanagedCallersOnly] Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(14, 6) ); @@ -9745,7 +9790,7 @@ public UnmanagedCallersOnlyAttribute() { } // (7,10): error CS8893: 'UnmanagedCallersOnlyAttribute' is not a valid calling convention type for 'UnmanagedCallersOnly'. // [UnmanagedCallersOnly(CallConvs = new[] { typeof(UnmanagedCallersOnlyAttribute) })] Diagnostic(ErrorCode.ERR_InvalidUnmanagedCallersOnlyCallConv, "UnmanagedCallersOnly(CallConvs = new[] { typeof(UnmanagedCallersOnlyAttribute) })").WithArguments("System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute").WithLocation(7, 10), - // (7,10): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. + // (7,10): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. // [UnmanagedCallersOnly(CallConvs = new[] { typeof(UnmanagedCallersOnlyAttribute) })] Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(7, 10) ); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index c73417b967423..2817592345629 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -4215,7 +4215,7 @@ static void Main() }"; var comp = CreateCompilation(new[] { source, UnmanagedCallersOnlyAttributeDefinition }, parseOptions: TestOptions.RegularPreview); comp.VerifyDiagnostics( - // (7,21): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static methods or static local functions. + // (7,21): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. // Action a = [UnmanagedCallersOnly] static () => { }; Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(7, 21)); } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index fd5ab55bf8be0..e0dd8695f7fa4 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -26479,5 +26479,265 @@ static void Interface.Method(int i) { } Diagnostic(ErrorCode.WRN_ExplicitImplCollision, "Method").WithArguments("Other.Interface.Method(int)").WithLocation(11, 37) ); } + + [Fact] + public void UnmanagedCallersOnly_01() + { + var source2 = @" +using System.Runtime.InteropServices; + +public interface I1 +{ + [UnmanagedCallersOnly] abstract static void M1(); + [UnmanagedCallersOnly] abstract static int operator +(I1 x); + [UnmanagedCallersOnly] abstract static int operator +(I1 x, I1 y); +} + +public interface I2 where T : I2 +{ + [UnmanagedCallersOnly] abstract static implicit operator int(T i); + [UnmanagedCallersOnly] abstract static explicit operator T(int i); +} +"; + + var compilation2 = CreateCompilation(new[] { source2, UnmanagedCallersOnlyAttributeDefinition }, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation2.VerifyDiagnostics( + // (6,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + // [UnmanagedCallersOnly] abstract static void M1(); + Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(6, 6), + // (7,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + // [UnmanagedCallersOnly] abstract static int operator +(I1 x); + Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(7, 6), + // (8,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + // [UnmanagedCallersOnly] abstract static int operator +(I1 x, I1 y); + Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(8, 6), + // (13,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + // [UnmanagedCallersOnly] abstract static implicit operator int(T i); + Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(13, 6), + // (14,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + // [UnmanagedCallersOnly] abstract static explicit operator T(int i); + Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(14, 6) + ); + } + + [Fact] + [WorkItem(54113, "https://github.com/dotnet/roslyn/issues/54113")] + public void UnmanagedCallersOnly_02() + { + var ilSource = @" +.class public auto ansi sealed beforefieldinit System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute + extends [mscorlib]System.Attribute +{ + .custom instance void [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype [mscorlib]System.AttributeTargets) = ( + 01 00 40 00 00 00 01 00 54 02 09 49 6e 68 65 72 + 69 74 65 64 00 + ) + .field public class [mscorlib]System.Type[] CallConvs + .field public string EntryPoint + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + ldarg.0 + call instance void [mscorlib]System.Attribute::.ctor() + ret + } +} + +.class interface public auto ansi abstract I1 +{ + .method public hidebysig abstract virtual static + void M1 () cil managed + { + // [System.Runtime.InteropServices.UnmanagedCallersOnly] + .custom instance void System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) + } + + .method public hidebysig specialname abstract virtual static + int32 op_UnaryPlus ( + class I1 x + ) cil managed + { + // [System.Runtime.InteropServices.UnmanagedCallersOnly] + .custom instance void System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) + } + + .method public hidebysig specialname abstract virtual static + int32 op_Addition ( + class I1 x, + class I1 y + ) cil managed + { + // [System.Runtime.InteropServices.UnmanagedCallersOnly] + .custom instance void System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) + } +} + +.class interface public auto ansi abstract I2`1<(class I2`1) T> +{ + .method public hidebysig specialname abstract virtual static + int32 op_Implicit ( + !T i + ) cil managed + { + // [System.Runtime.InteropServices.UnmanagedCallersOnly] + .custom instance void System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) + } + + .method public hidebysig specialname abstract virtual static + !T op_Explicit ( + int32 i + ) cil managed + { + // [System.Runtime.InteropServices.UnmanagedCallersOnly] + .custom instance void System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) + } +} +"; + + var source1 = +@" +class Test +{ + static void M02(T x, T y) where T : I1 + { + T.M1(); + _ = +x; + _ = x + y; + } + + static int M03(T x) where T : I2 + { + _ = (T)x; + return x; + } +} +"; + var compilation1 = CreateCompilationWithIL(source1, ilSource, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + // Conversions aren't flagged due to https://github.com/dotnet/roslyn/issues/54113. + compilation1.VerifyDiagnostics( + // (6,11): error CS0570: 'I1.M1()' is not supported by the language + // T.M1(); + Diagnostic(ErrorCode.ERR_BindToBogus, "M1").WithArguments("I1.M1()").WithLocation(6, 11), + // (7,13): error CS0570: 'I1.operator +(I1)' is not supported by the language + // _ = +x; + Diagnostic(ErrorCode.ERR_BindToBogus, "+x").WithArguments("I1.operator +(I1)").WithLocation(7, 13), + // (8,13): error CS0570: 'I1.operator +(I1, I1)' is not supported by the language + // _ = x + y; + Diagnostic(ErrorCode.ERR_BindToBogus, "x + y").WithArguments("I1.operator +(I1, I1)").WithLocation(8, 13) + ); + } + + [Fact] + public void UnmanagedCallersOnly_03() + { + var source2 = @" +using System.Runtime.InteropServices; + +public interface I1 where T : I1 +{ + abstract static void M1(); + abstract static int operator +(T x); + abstract static int operator +(T x, T y); + abstract static implicit operator int(T i); + abstract static explicit operator T(int i); +} + +class C : I1 +{ + [UnmanagedCallersOnly] public static void M1() {} + [UnmanagedCallersOnly] public static int operator +(C x) => 0; + [UnmanagedCallersOnly] public static int operator +(C x, C y) => 0; + [UnmanagedCallersOnly] public static implicit operator int(C i) => 0; + [UnmanagedCallersOnly] public static explicit operator C(int i) => null; +} +"; + + var compilation2 = CreateCompilation(new[] { source2, UnmanagedCallersOnlyAttributeDefinition }, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation2.VerifyDiagnostics( + // (15,47): error CS8932: 'UnmanagedCallersOnly' method 'C.M1()' cannot implement interface member 'I1.M1()' in type 'C' + // [UnmanagedCallersOnly] public static void M1() {} + Diagnostic(ErrorCode.ERR_InterfaceImplementedByUnmanagedCallersOnlyMethod, "M1").WithArguments("C.M1()", "I1.M1()", "C").WithLocation(15, 47), + // (16,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + // [UnmanagedCallersOnly] public static int operator +(C x) => 0; + Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(16, 6), + // (17,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + // [UnmanagedCallersOnly] public static int operator +(C x, C y) => 0; + Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(17, 6), + // (18,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + // [UnmanagedCallersOnly] public static implicit operator int(C i) => 0; + Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(18, 6), + // (19,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + // [UnmanagedCallersOnly] public static explicit operator C(int i) => null; + Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(19, 6) + ); + } + + [Fact] + public void UnmanagedCallersOnly_04() + { + var source2 = @" +using System.Runtime.InteropServices; + +public interface I1 where T : I1 +{ + abstract static void M1(); + abstract static int operator +(T x); + abstract static int operator +(T x, T y); + abstract static implicit operator int(T i); + abstract static explicit operator T(int i); +} + +class C : I1 +{ + [UnmanagedCallersOnly] static void I1.M1() {} + [UnmanagedCallersOnly] static int I1.operator +(C x) => 0; + [UnmanagedCallersOnly] static int I1.operator +(C x, C y) => 0; + [UnmanagedCallersOnly] static implicit I1.operator int(C i) => 0; + [UnmanagedCallersOnly] static explicit I1.operator C(int i) => null; +} +"; + + var compilation2 = CreateCompilation(new[] { source2, UnmanagedCallersOnlyAttributeDefinition }, options: TestOptions.DebugDll, + parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.NetCoreApp); + + compilation2.VerifyDiagnostics( + // (15,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + // [UnmanagedCallersOnly] static void I1.M1() {} + Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(15, 6), + // (16,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + // [UnmanagedCallersOnly] static int I1.operator +(C x) => 0; + Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(16, 6), + // (17,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + // [UnmanagedCallersOnly] static int I1.operator +(C x, C y) => 0; + Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(17, 6), + // (18,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + // [UnmanagedCallersOnly] static implicit I1.operator int(C i) => 0; + Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(18, 6), + // (19,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions. + // [UnmanagedCallersOnly] static explicit I1.operator C(int i) => null; + Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "UnmanagedCallersOnly").WithLocation(19, 6) + ); + } } } From e2d605ec1e3682b7931f46147747127149d3bd35 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 15 Jun 2021 14:16:14 -0700 Subject: [PATCH 124/127] 'with' on anonymous type needs to lower values (#54086) --- .../LocalRewriter_ObjectCreationExpression.cs | 3 +- .../Semantic/Semantics/RecordStructTests.cs | 29 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectCreationExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectCreationExpression.cs index b8449b4d8b567..0b2fa33ff14e2 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectCreationExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectCreationExpression.cs @@ -183,7 +183,8 @@ ImmutableArray getAnonymousTypeValues(BoundWithExpression withE Debug.Assert(left.MemberSymbol is not null); // We evaluate the values provided in source first - BoundLocal valueTemp = _factory.StoreToTemp(assignment.Right, out BoundAssignmentOperator boundAssignmentToTemp); + var rewrittenRight = VisitExpression(assignment.Right); + BoundLocal valueTemp = _factory.StoreToTemp(rewrittenRight, out BoundAssignmentOperator boundAssignmentToTemp); temps.Add(valueTemp.LocalSymbol); sideEffects.Add(boundAssignmentToTemp); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs index 4fe37305c5c55..6ba5ae11f78a7 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs @@ -9993,6 +9993,35 @@ public static void M() VerifyFlowGraphAndDiagnosticsForTest(src, expectedFlowGraph, expectedDiagnostics, parseOptions: TestOptions.RegularPreview); } + [Fact, WorkItem(53849, "https://github.com/dotnet/roslyn/issues/53849")] + public void WithExpr_AnonymousType_ValueIsLoweredToo() + { + var src = @" +var x = new { Property = 42 }; +var adjusted = x with { Property = x.Property + 2 }; + +System.Console.WriteLine(adjusted); +"; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreview); + var verifier = CompileAndVerify(comp, expectedOutput: "{ Property = 44 }"); + verifier.VerifyDiagnostics(); + } + + [Fact, WorkItem(53849, "https://github.com/dotnet/roslyn/issues/53849")] + public void WithExpr_AnonymousType_ValueIsLoweredToo_NestedWith() + { + var src = @" +var x = new { Property = 42 }; +var container = new { Item = x }; +var adjusted = container with { Item = x with { Property = x.Property + 2 } }; + +System.Console.WriteLine(adjusted); +"; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreview); + var verifier = CompileAndVerify(comp, expectedOutput: "{ Item = { Property = 44 } }"); + verifier.VerifyDiagnostics(); + } + [Fact] public void AttributesOnPrimaryConstructorParameters_01() { From 6b93b0c68964af081b3e8deb8d64ef78510eb80a Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 15 Jun 2021 14:16:48 -0700 Subject: [PATCH 125/127] Use new formatting options in roslyn itself. (#53957) * Add formatting options * Update codestyle version * Update version * enable * keep together * Fix warning * Formatting style * Formatting style * Formatting style * Formatting style * Formatting style * Formatting style * Formatting style * Formatting style * Formatting style * Formatting style * Formatting style * Formatting style * Formatting style * Revert * Disable * Place colon consistently * Fix IDE0059: Unnecessary assignment * Fix IDE0059: Unnecessary assignment * Fix IDE0059: Unnecessary assignment * Fix IDE0059: Unnecessary assignment * Fix IDE0059: Unnecessary assignment * Fix IDE0059: Unnecessary assignment * Fix IDE0059: Unnecessary assignment * Fix IDE0059: Unnecessary assignment * Revert * Disable * Disable * Disable * Disable * Fix IDE0059: Unnecessary assignment * Fix IDE0059: Unnecessary assignment * Fix IDE0059: Unnecessary assignment * Fix IDE0059: Unnecessary assignment * Fix IDE0059: Unnecessary assignment * Fix IDE0059: Unnecessary assignment * Fix IDE0059: Unnecessary assignment * Update .editorconfig * Update .editorconfig * move options * Only run on IDE code --- .editorconfig | 24 +++++++++++++++++++ eng/Versions.props | 2 +- .../Portable/FindUsages/DefinitionItem.cs | 4 ++-- .../MiscellaneousDiagnosticListTable.cs | 4 ++-- .../MiscellaneousTodoListTable.cs | 4 ++-- .../VisualStudioBaseDiagnosticListTable.cs | 4 ++-- ...iagnosticListTable.BuildTableDataSource.cs | 4 ++-- .../VisualStudioDiagnosticListTable.cs | 12 +++++----- .../VisualStudioTodoListTable.cs | 4 ++-- .../Impl/Client/RemoteDiagnosticListTable.cs | 4 ++-- .../IncrementalAnalyzerProviderMetadata.cs | 8 +++---- .../EventListener/EventListenerMetadata.cs | 8 +++---- .../DiagnosticAnalyzer/PerformanceQueue.cs | 4 ++-- .../PerformanceTrackerService.cs | 4 ++-- 14 files changed, 57 insertions(+), 33 deletions(-) diff --git a/.editorconfig b/.editorconfig index 7400f6df98b17..89133f643dda1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -61,6 +61,10 @@ dotnet_style_coalesce_expression = true:suggestion dotnet_style_null_propagation = true:suggestion dotnet_style_explicit_tuple_names = true:suggestion +# Whitespace options +dotnet_style_allow_multiple_blank_lines_experimental = false +dotnet_style_allow_statement_immediately_after_block_experimental = true + # Non-private static fields are PascalCase dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields @@ -182,6 +186,11 @@ csharp_indent_case_contents_when_block = true csharp_indent_switch_labels = true csharp_indent_labels = flush_left +# Whitespace options +csharp_style_allow_embedded_statements_on_same_line_experimental = false +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false + # Prefer "var" everywhere csharp_style_var_for_built_in_types = true:suggestion csharp_style_var_when_type_is_apparent = true:suggestion @@ -272,6 +281,21 @@ csharp_style_var_for_built_in_types = true:warning csharp_style_var_when_type_is_apparent = true:warning csharp_style_var_elsewhere = true:warning +# dotnet_style_allow_multiple_blank_lines_experimental +dotnet_diagnostic.IDE2000.severity = warning + +# csharp_style_allow_embedded_statements_on_same_line_experimental +dotnet_diagnostic.IDE2001.severity = warning + +# csharp_style_allow_blank_lines_between_consecutive_braces_experimental +dotnet_diagnostic.IDE2002.severity = warning + +# dotnet_style_allow_statement_immediately_after_block_experimental +dotnet_diagnostic.IDE2003.severity = none + +# csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental +dotnet_diagnostic.IDE2004.severity = warning + [src/{VisualStudio}/**/*.{cs,vb}] # CA1822: Make member static # Not enforced as a build 'warning' for 'VisualStudio' layer due to large number of false positives from https://github.com/dotnet/roslyn-analyzers/issues/3857 and https://github.com/dotnet/roslyn-analyzers/issues/3858 diff --git a/eng/Versions.props b/eng/Versions.props index 5b00a7d5603e9..241e4ad538cc6 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -30,7 +30,7 @@ 3.3.3-beta1.21105.3 6.0.0-preview1.21054.10 1.0.1-beta1.20623.3 - 3.9.0 + 3.10.0 16.10.230 5.0.0-alpha1.19409.1 5.0.0-preview.1.20112.8 diff --git a/src/Features/Core/Portable/FindUsages/DefinitionItem.cs b/src/Features/Core/Portable/FindUsages/DefinitionItem.cs index 95640414d3ea6..c12672789b0ea 100644 --- a/src/Features/Core/Portable/FindUsages/DefinitionItem.cs +++ b/src/Features/Core/Portable/FindUsages/DefinitionItem.cs @@ -117,8 +117,8 @@ protected DefinitionItem( ImmutableArray originationParts, ImmutableArray sourceSpans, ImmutableDictionary properties, - bool displayIfNoReferences) : - this( + bool displayIfNoReferences) + : this( tags, displayParts, nameDisplayParts, diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/MiscellaneousDiagnosticListTable.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/MiscellaneousDiagnosticListTable.cs index d58222681a904..18e29d1645198 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/MiscellaneousDiagnosticListTable.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/MiscellaneousDiagnosticListTable.cs @@ -34,8 +34,8 @@ private sealed class MiscellaneousDiagnosticListTable : VisualStudioBaseDiagnost { private readonly LiveTableDataSource _source; - public MiscellaneousDiagnosticListTable(Workspace workspace, IDiagnosticService diagnosticService, ITableManagerProvider provider) : - base(workspace, provider) + public MiscellaneousDiagnosticListTable(Workspace workspace, IDiagnosticService diagnosticService, ITableManagerProvider provider) + : base(workspace, provider) { _source = new LiveTableDataSource(workspace, diagnosticService, IdentifierString); diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/MiscellaneousTodoListTable.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/MiscellaneousTodoListTable.cs index 8a92d5d82de14..6a431243b7fa2 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/MiscellaneousTodoListTable.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/MiscellaneousTodoListTable.cs @@ -31,8 +31,8 @@ public void StartListening(Workspace workspace, ITodoListProvider service) private sealed class MiscellaneousTodoListTable : VisualStudioBaseTodoListTable { - public MiscellaneousTodoListTable(Workspace workspace, ITodoListProvider todoListProvider, ITableManagerProvider provider) : - base(workspace, todoListProvider, IdentifierString, provider) + public MiscellaneousTodoListTable(Workspace workspace, ITodoListProvider todoListProvider, ITableManagerProvider provider) + : base(workspace, todoListProvider, IdentifierString, provider) { ConnectWorkspaceEvents(); } diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseDiagnosticListTable.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseDiagnosticListTable.cs index 18186f1bfa44f..9ad21793b05f3 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseDiagnosticListTable.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseDiagnosticListTable.cs @@ -34,8 +34,8 @@ internal abstract partial class VisualStudioBaseDiagnosticListTable : AbstractTa StandardTableColumnDefinitions.SuppressionState }; - protected VisualStudioBaseDiagnosticListTable(Workspace workspace, ITableManagerProvider provider) : - base(workspace, provider, StandardTables.ErrorsTable) + protected VisualStudioBaseDiagnosticListTable(Workspace workspace, ITableManagerProvider provider) + : base(workspace, provider, StandardTables.ErrorsTable) { } diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.BuildTableDataSource.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.BuildTableDataSource.cs index bb04f29635839..351cfcc868b05 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.BuildTableDataSource.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.BuildTableDataSource.cs @@ -126,8 +126,8 @@ private class TableEntriesSnapshot : AbstractTableEntriesSnapshot items) : - base(version, items, ImmutableArray.Empty) + DiagnosticTableEntriesSource source, int version, ImmutableArray items) + : base(version, items, ImmutableArray.Empty) { _source = source; } diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.cs index 0401859b94c41..1ff234ca8b85c 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.cs @@ -79,8 +79,8 @@ public VisualStudioDiagnosticListTable( VisualStudioWorkspaceImpl workspace, IDiagnosticService diagnosticService, ITableManagerProvider provider, - IErrorList errorList) : - base(workspace, provider) + IErrorList errorList) + : base(workspace, provider) { _errorList = errorList; @@ -104,15 +104,15 @@ private ITableDataSource GetCurrentDataSource() } /// this is for test only - internal VisualStudioDiagnosticListTable(Workspace workspace, IDiagnosticService diagnosticService, ITableManagerProvider provider) : - base(workspace, provider) + internal VisualStudioDiagnosticListTable(Workspace workspace, IDiagnosticService diagnosticService, ITableManagerProvider provider) + : base(workspace, provider) { AddInitialTableSource(workspace.CurrentSolution, new LiveTableDataSource(workspace, diagnosticService, IdentifierString)); } /// this is for test only - internal VisualStudioDiagnosticListTable(Workspace workspace, ExternalErrorDiagnosticUpdateSource errorSource, ITableManagerProvider provider) : - base(workspace, provider) + internal VisualStudioDiagnosticListTable(Workspace workspace, ExternalErrorDiagnosticUpdateSource errorSource, ITableManagerProvider provider) + : base(workspace, provider) { AddInitialTableSource(workspace.CurrentSolution, new BuildTableDataSource(workspace, errorSource)); } diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioTodoListTable.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioTodoListTable.cs index a6e98ac5c464f..850ca550b0157 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioTodoListTable.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioTodoListTable.cs @@ -32,8 +32,8 @@ public void StartListening(Workspace workspace, ITodoListProvider service) internal class VisualStudioTodoListTable : VisualStudioBaseTodoListTable { // internal for testing - internal VisualStudioTodoListTable(Workspace workspace, ITodoListProvider todoListProvider, ITableManagerProvider provider) : - base(workspace, todoListProvider, IdentifierString, provider) + internal VisualStudioTodoListTable(Workspace workspace, ITodoListProvider todoListProvider, ITableManagerProvider provider) + : base(workspace, todoListProvider, IdentifierString, provider) { ConnectWorkspaceEvents(); } diff --git a/src/VisualStudio/LiveShare/Impl/Client/RemoteDiagnosticListTable.cs b/src/VisualStudio/LiveShare/Impl/Client/RemoteDiagnosticListTable.cs index 5d407bad2c7c9..52c3a081fd895 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/RemoteDiagnosticListTable.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/RemoteDiagnosticListTable.cs @@ -30,8 +30,8 @@ internal class RemoteDiagnosticListTable : VisualStudioBaseDiagnosticListTable [ImportingConstructor] [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Incorrectly used in production code: https://github.com/dotnet/roslyn/issues/42839")] public RemoteDiagnosticListTable( - SVsServiceProvider serviceProvider, RemoteLanguageServiceWorkspace workspace, IDiagnosticService diagnosticService, ITableManagerProvider provider) : - base(workspace, provider) + SVsServiceProvider serviceProvider, RemoteLanguageServiceWorkspace workspace, IDiagnosticService diagnosticService, ITableManagerProvider provider) + : base(workspace, provider) { _source = new LiveTableDataSource(workspace, diagnosticService, IdentifierString); AddInitialTableSource(workspace.CurrentSolution, _source); diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IncrementalAnalyzerProviderMetadata.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IncrementalAnalyzerProviderMetadata.cs index f6a8f05de4c65..af4b6cc9ba553 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IncrementalAnalyzerProviderMetadata.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IncrementalAnalyzerProviderMetadata.cs @@ -15,15 +15,15 @@ internal class IncrementalAnalyzerProviderMetadata : WorkspaceKindMetadata public bool HighPriorityForActiveFile { get; } public string Name { get; } - public IncrementalAnalyzerProviderMetadata(IDictionary data) : - base(data) + public IncrementalAnalyzerProviderMetadata(IDictionary data) + : base(data) { this.HighPriorityForActiveFile = (bool)data.GetValueOrDefault("HighPriorityForActiveFile"); this.Name = (string)data.GetValueOrDefault("Name"); } - public IncrementalAnalyzerProviderMetadata(string name, bool highPriorityForActiveFile, params string[] workspaceKinds) : - base(workspaceKinds) + public IncrementalAnalyzerProviderMetadata(string name, bool highPriorityForActiveFile, params string[] workspaceKinds) + : base(workspaceKinds) { this.HighPriorityForActiveFile = highPriorityForActiveFile; this.Name = name; diff --git a/src/Workspaces/Core/Portable/Workspace/Host/EventListener/EventListenerMetadata.cs b/src/Workspaces/Core/Portable/Workspace/Host/EventListener/EventListenerMetadata.cs index 1d92e0042cdf6..51e54e6b4a3a7 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/EventListener/EventListenerMetadata.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/EventListener/EventListenerMetadata.cs @@ -18,14 +18,14 @@ internal class EventListenerMetadata : WorkspaceKindMetadata { public string Service { get; } - public EventListenerMetadata(IDictionary data) : - base(data) + public EventListenerMetadata(IDictionary data) + : base(data) { this.Service = (string)data.GetValueOrDefault("Service"); } - public EventListenerMetadata(string service, params string[] workspaceKinds) : - base(workspaceKinds) + public EventListenerMetadata(string service, params string[] workspaceKinds) + : base(workspaceKinds) { if (workspaceKinds?.Length == 0) { diff --git a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceQueue.cs b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceQueue.cs index 6693cd16b0753..92b5a15249b5f 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceQueue.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceQueue.cs @@ -125,8 +125,8 @@ private class Snapshot /// private readonly Dictionary _performanceMap; - public Snapshot(IEnumerable<(string analyzerId, TimeSpan timeSpan)> snapshot, int unitCount) : - this(Convert(snapshot), unitCount) + public Snapshot(IEnumerable<(string analyzerId, TimeSpan timeSpan)> snapshot, int unitCount) + : this(Convert(snapshot), unitCount) { } diff --git a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceTrackerService.cs b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceTrackerService.cs index d6cb2cde53457..2cfbc915461e0 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceTrackerService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceTrackerService.cs @@ -44,8 +44,8 @@ internal class PerformanceTrackerService : IPerformanceTrackerService [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public PerformanceTrackerService() : - this(DefaultMinLOFValue, DefaultAverageThreshold, DefaultStddevThreshold) + public PerformanceTrackerService() + : this(DefaultMinLOFValue, DefaultAverageThreshold, DefaultStddevThreshold) { } From 9b0752c53b1ae89734f5cb4c24401049ee422170 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Thu, 17 Jun 2021 02:34:06 +1000 Subject: [PATCH 126/127] EnC - Update CustomAttributes table instead of just always adding to it (#53507) --- .../EditAndContinueClosureTests.cs | 34 +- .../EditAndContinueStateMachineTests.cs | 94 +-- .../EditAndContinue/EditAndContinueTests.cs | 690 +++++++++++++++++- .../EditAndContinue/DeltaMetadataWriter.cs | 152 +++- .../Emit/EditAndContinue/EmitBaseline.cs | 6 + .../Emit/EditAndContinue/SymbolMatcher.cs | 1 + .../Core/Portable/PEWriter/MetadataWriter.cs | 38 +- .../EditAndContinueClosureTests.vb | 8 +- .../EditAndContinueStateMachineTests.vb | 151 ++-- 9 files changed, 1003 insertions(+), 171 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs index 003401a3770ab..16def4e4ea2af 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs @@ -309,7 +309,7 @@ void F() CheckEncLogDefinitions(reader1, Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); } [Fact] @@ -362,7 +362,7 @@ void F() CheckEncLogDefinitions(reader1, Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); var testData0 = new CompilationTestData(); var bytes0 = compilation0.EmitToArray(testData: testData0); @@ -477,7 +477,7 @@ void F() Row(2, TableIndex.StandAloneSig, EditAndContinueOperation.Default), Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); } [Fact] @@ -532,7 +532,7 @@ int F(int a) Row(2, TableIndex.StandAloneSig, EditAndContinueOperation.Default), Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); } [Fact] @@ -586,7 +586,7 @@ int F(int a) Row(2, TableIndex.StandAloneSig, EditAndContinueOperation.Default), Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); } [Fact] @@ -786,7 +786,7 @@ int F(int a) Row(2, TableIndex.StandAloneSig, EditAndContinueOperation.Default), Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); } [Fact] @@ -1076,8 +1076,8 @@ void F() Row(18, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(19, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(20, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(17, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(18, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(15, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(16, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); } [Fact] @@ -1153,7 +1153,7 @@ void F() Row(10, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(12, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(15, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(15, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(14, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); } [Fact] @@ -1240,9 +1240,9 @@ public void F() Row(8, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(10, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(12, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(10, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(11, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(12, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(9, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); } [Fact] @@ -1322,7 +1322,7 @@ public void F() Row(6, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(9, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(10, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); } [Fact] @@ -1399,7 +1399,7 @@ public void F() Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(6, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(9, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); } [Fact] @@ -4002,7 +4002,7 @@ .maxstack 2 Row(2, TableIndex.StandAloneSig, EditAndContinueOperation.Default), Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); } [Fact] @@ -4087,7 +4087,7 @@ .maxstack 2 Row(2, TableIndex.StandAloneSig, EditAndContinueOperation.Default), Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); } [Fact] @@ -4160,7 +4160,7 @@ .maxstack 2 CheckEncLogDefinitions(reader1, Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs index 7dae6013bdcab..77d26eb3af244 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs @@ -536,9 +536,13 @@ static IEnumerable F() using (var md1 = diff1.GetMetadata()) { + CheckAttributes(md1.Reader, + new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef))); // row id 0 == delete + CheckEncLogDefinitions(md1.Reader, Row(3, TableIndex.StandAloneSig, EditAndContinueOperation.Default), - Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default)); + Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(1, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); // Delete IteratorStateMachineAttribute } } } @@ -583,9 +587,15 @@ static Task F() using (var md1 = diff1.GetMetadata()) { + CheckAttributes(md1.Reader, + new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // row id 0 == delete + new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef))); // row id 0 == delete + CheckEncLogDefinitions(md1.Reader, Row(3, TableIndex.StandAloneSig, EditAndContinueOperation.Default), - Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default)); + Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(1, TableIndex.CustomAttribute, EditAndContinueOperation.Default), // Delete AsyncStateMachineAttribute + Row(2, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); // Delete DebuggerStepThroughAttribute } } } @@ -674,12 +684,12 @@ static async Task F(int a) Row(6, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(9, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(12, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(16, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(17, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(18, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(19, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(20, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(21, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(1, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(2, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(8, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(9, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); } } } @@ -738,8 +748,8 @@ static IEnumerable F() Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(5, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(13, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(14, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(1, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); diff1.VerifyIL("C.d__0.System.Collections.IEnumerator.MoveNext", @" { @@ -875,8 +885,8 @@ static async Task F() Row(4, TableIndex.StandAloneSig, EditAndContinueOperation.Default), Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(8, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(9, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(1, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(2, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); diff1.VerifyIL("C.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" { @@ -1146,8 +1156,8 @@ static IEnumerable F(int p) Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(5, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(13, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(14, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(1, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); diff1.VerifyIL("C.d__0.System.Collections.IEnumerator.MoveNext", @" { @@ -1250,8 +1260,8 @@ static IEnumerable F(int p) Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(5, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(13, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(14, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(1, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); diff1.VerifyIL("C.d__0.System.Collections.IEnumerator.MoveNext", @" { @@ -1361,8 +1371,8 @@ static IEnumerable F(int p) Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(5, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(13, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(14, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(1, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); diff1.VerifyIL("C.d__0.System.Collections.IEnumerator.MoveNext", @" { @@ -1469,8 +1479,8 @@ static IEnumerable F() Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(5, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(13, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(14, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(1, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); diff1.VerifyIL("C.d__0.System.Collections.IEnumerator.MoveNext", @" { @@ -1586,8 +1596,8 @@ static IEnumerable F() Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(5, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(13, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(14, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(1, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); diff1.VerifyIL("C.d__0.System.Collections.IEnumerator.MoveNext", @" { @@ -1879,10 +1889,10 @@ static async Task H() // updated Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(6, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(9, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(16, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(17, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(18, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(19, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(1, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(2, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); diff1.VerifyIL("C.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" { @@ -1990,8 +2000,8 @@ .locals init (int V_0, Row(18, TableIndex.Field, EditAndContinueOperation.Default), Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(6, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(20, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(21, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(1, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(2, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); diff2.VerifyIL("C.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" { @@ -2103,10 +2113,10 @@ .locals init (int V_0, Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(9, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(12, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(22, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(23, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(24, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(25, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(8, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(9, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); } [Fact] @@ -2626,10 +2636,10 @@ static async Task H() // updated Row(5, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(9, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(12, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(16, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(17, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(18, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(19, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(8, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(9, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); // Note that the new awaiter is allocated slot <>u__3 since <>u__1 and <>u__2 are taken. diff1.VerifyIL("C.d__3.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" @@ -2766,8 +2776,8 @@ .locals init (int V_0, Row(12, TableIndex.Field, EditAndContinueOperation.Default), Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(9, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(20, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(21, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); // Note that the new awaiters are allocated slots <>u__4, <>u__5. diff2.VerifyIL("C.d__3.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" @@ -2910,10 +2920,10 @@ .locals init (int V_0, Row(6, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(12, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(15, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(22, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(23, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(24, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(25, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); + Row(8, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(9, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(11, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(12, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs index 0652a0d96bc13..3a6e5142eb10f 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs @@ -359,6 +359,692 @@ static void Main() { } Handle(2, TableIndex.AssemblyRef)); } + [Fact] + public void ModifyMethod_WithAttributes1() + { + var source0 = +@"class C +{ + static void Main() { } + [System.ComponentModel.Description(""The F method"")] + static string F() { return null; } +}"; + var source1 = +@"class C +{ + static void Main() { } + [System.ComponentModel.Description(""The F method"")] + static string F() { return string.Empty; } +}"; + var source2 = +@"[System.ComponentModel.Browsable(false)] +class C +{ + static void Main() { } + [System.ComponentModel.Description(""The F method""), System.ComponentModel.Category(""Methods"")] + static string F() { return string.Empty; } +}"; + var source3 = +@"[System.ComponentModel.Browsable(false)] +class C +{ + static void Main() { } + [System.ComponentModel.Browsable(false), System.ComponentModel.Description(""The F method""), System.ComponentModel.Category(""Methods"")] + static string F() { return string.Empty; } +}"; + + var compilation0 = CreateCompilation(source0, options: TestOptions.DebugExe, targetFramework: TargetFramework.NetStandard20); + var compilation1 = compilation0.WithSource(source1); + var compilation2 = compilation1.WithSource(source2); + var compilation3 = compilation2.WithSource(source3); + + var method0 = compilation0.GetMember("C.F"); + + // Verify full metadata contains expected rows. + var bytes0 = compilation0.EmitToArray(); + using var md0 = ModuleMetadata.CreateFromImage(bytes0); + var reader0 = md0.MetadataReader; + + CheckNames(reader0, reader0.GetTypeDefNames(), "", "C"); + CheckNames(reader0, reader0.GetMethodDefNames(), "Main", "F", ".ctor"); + CheckNames(reader0, reader0.GetMemberRefNames(), /*CompilationRelaxationsAttribute.*/".ctor", /*RuntimeCompatibilityAttribute.*/".ctor", /*Object.*/".ctor", /*DebuggableAttribute*/".ctor", /*DescriptionAttribute*/".ctor"); + + Assert.Equal(4, reader0.CustomAttributes.Count); + + var generation0 = EmitBaseline.CreateInitialBaseline( + md0, + EmptyLocalsProvider); + var method1 = compilation1.GetMember("C.F"); + + var diff1 = compilation1.EmitDifference( + generation0, + ImmutableArray.Create(SemanticEdit.Create(SemanticEditKind.Update, method0, method1))); + + // Verify delta metadata contains expected rows. + using var md1 = diff1.GetMetadata(); + var reader1 = md1.Reader; + var readers = new[] { reader0, reader1 }; + + EncValidation.VerifyModuleMvid(1, reader0, reader1); + + CheckNames(readers, reader1.GetTypeDefNames()); + CheckNames(readers, reader1.GetMethodDefNames(), "F"); + CheckNames(readers, reader1.GetMemberRefNames(), /*DescriptionAttribute*/".ctor", /*String.*/"Empty"); + + Assert.Equal(1, reader1.CustomAttributes.Count); + + CheckEncLog(reader1, + Row(2, TableIndex.AssemblyRef, EditAndContinueOperation.Default), + Row(6, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(7, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(7, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(8, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(9, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(2, TableIndex.StandAloneSig, EditAndContinueOperation.Default), + Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); // Row 4, so updating existing CustomAttribute + + CheckEncMap(reader1, + Handle(7, TableIndex.TypeRef), + Handle(8, TableIndex.TypeRef), + Handle(9, TableIndex.TypeRef), + Handle(2, TableIndex.MethodDef), + Handle(6, TableIndex.MemberRef), + Handle(7, TableIndex.MemberRef), + Handle(4, TableIndex.CustomAttribute), + Handle(2, TableIndex.StandAloneSig), + Handle(2, TableIndex.AssemblyRef)); + + var method2 = compilation2.GetMember("C.F"); + var diff2 = compilation2.EmitDifference( + diff1.NextGeneration, + ImmutableArray.Create( + SemanticEdit.Create(SemanticEditKind.Update, compilation1.GetMember("C"), compilation2.GetMember("C")), + SemanticEdit.Create(SemanticEditKind.Update, method1, method2))); + + // Verify delta metadata contains expected rows. + using var md2 = diff2.GetMetadata(); + var reader2 = md2.Reader; + readers = new[] { reader0, reader1, reader2 }; + + CheckNames(readers, reader2.GetTypeDefNames(), "C"); + CheckNames(readers, reader2.GetMethodDefNames(), "F"); + CheckNames(readers, reader2.GetMemberRefNames(), /*DescriptionAttribute*/".ctor", /*BrowsableAttribute*/".ctor", /*CategoryAttribute*/".ctor", /*String.*/"Empty"); + + Assert.Equal(3, reader2.CustomAttributes.Count); + + CheckEncLog(reader2, + Row(3, TableIndex.AssemblyRef, EditAndContinueOperation.Default), + Row(8, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(9, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(10, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(11, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(10, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(11, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(12, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(13, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(14, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(3, TableIndex.StandAloneSig, EditAndContinueOperation.Default), + Row(2, TableIndex.TypeDef, EditAndContinueOperation.Default), + Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), // Row 4, updating the existing custom attribute + Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default), // Row 5, adding a new CustomAttribute + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); // Row 6 adding a new CustomAttribute + + CheckEncMap(reader2, + Handle(10, TableIndex.TypeRef), + Handle(11, TableIndex.TypeRef), + Handle(12, TableIndex.TypeRef), + Handle(13, TableIndex.TypeRef), + Handle(14, TableIndex.TypeRef), + Handle(2, TableIndex.TypeDef), + Handle(2, TableIndex.MethodDef), + Handle(8, TableIndex.MemberRef), + Handle(9, TableIndex.MemberRef), + Handle(10, TableIndex.MemberRef), + Handle(11, TableIndex.MemberRef), + Handle(4, TableIndex.CustomAttribute), + Handle(5, TableIndex.CustomAttribute), + Handle(6, TableIndex.CustomAttribute), + Handle(3, TableIndex.StandAloneSig), + Handle(3, TableIndex.AssemblyRef)); + + var method3 = compilation3.GetMember("C.F"); + var diff3 = compilation3.EmitDifference( + diff2.NextGeneration, + ImmutableArray.Create(SemanticEdit.Create(SemanticEditKind.Update, method2, method3))); + + // Verify delta metadata contains expected rows. + using var md3 = diff3.GetMetadata(); + var reader3 = md3.Reader; + readers = new[] { reader0, reader1, reader2, reader3 }; + + CheckNames(readers, reader3.GetTypeDefNames()); + CheckNames(readers, reader3.GetMethodDefNames(), "F"); + CheckNames(readers, reader3.GetMemberRefNames(), /*BrowsableAttribute*/".ctor", /*DescriptionAttribute*/".ctor", /*CategoryAttribute*/".ctor", /*String.*/"Empty"); + + CheckEncLog(reader3, + Row(4, TableIndex.AssemblyRef, EditAndContinueOperation.Default), + Row(12, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(13, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(14, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(15, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(15, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(16, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(17, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(18, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(19, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(4, TableIndex.StandAloneSig, EditAndContinueOperation.Default), + Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), // Row 4, updating the existing custom attribute + Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default), // Row 5, updating a row that was new in Generation 2 + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); // Row 7, adding a new CustomAttribute, and skipping row 6 which is not for the method being emitted + + CheckEncMap(reader3, + Handle(15, TableIndex.TypeRef), + Handle(16, TableIndex.TypeRef), + Handle(17, TableIndex.TypeRef), + Handle(18, TableIndex.TypeRef), + Handle(19, TableIndex.TypeRef), + Handle(2, TableIndex.MethodDef), + Handle(12, TableIndex.MemberRef), + Handle(13, TableIndex.MemberRef), + Handle(14, TableIndex.MemberRef), + Handle(15, TableIndex.MemberRef), + Handle(4, TableIndex.CustomAttribute), + Handle(5, TableIndex.CustomAttribute), + Handle(7, TableIndex.CustomAttribute), + Handle(4, TableIndex.StandAloneSig), + Handle(4, TableIndex.AssemblyRef)); + } + + [Fact] + public void ModifyMethod_WithAttributes2() + { + var source0 = +@"[System.ComponentModel.Browsable(false)] +class C +{ + static void Main() { } + [System.ComponentModel.Description(""The F method"")] + static string F() { return null; } +} +[System.ComponentModel.Browsable(false)] +class D +{ + [System.ComponentModel.Description(""A"")] + static string A() { return null; } +} +"; + var source1 = +@" +[System.ComponentModel.Browsable(false)] +class C +{ + static void Main() { } + [System.ComponentModel.Description(""The F method""), System.ComponentModel.Browsable(false), System.ComponentModel.Category(""Methods"")] + static string F() { return null; } +} +[System.ComponentModel.Browsable(false)] +class D +{ + [System.ComponentModel.Description(""A""), System.ComponentModel.Category(""Methods"")] + static string A() { return null; } +} +"; + + var compilation0 = CreateCompilation(source0, options: TestOptions.DebugExe, targetFramework: TargetFramework.NetStandard20); + var compilation1 = compilation0.WithSource(source1); + + var method0_1 = compilation0.GetMember("C.F"); + var method0_2 = compilation0.GetMember("D.A"); + + // Verify full metadata contains expected rows. + var bytes0 = compilation0.EmitToArray(); + using var md0 = ModuleMetadata.CreateFromImage(bytes0); + var reader0 = md0.MetadataReader; + + CheckNames(reader0, reader0.GetTypeDefNames(), "", "C", "D"); + CheckNames(reader0, reader0.GetMethodDefNames(), "Main", "F", ".ctor", "A", ".ctor"); + CheckNames(reader0, reader0.GetMemberRefNames(), /*CompilationRelaxationsAttribute.*/".ctor", /*RuntimeCompatibilityAttribute.*/".ctor", /*Object.*/".ctor", /*DebuggableAttribute*/".ctor", /*DescriptionAttribute*/".ctor", /*BrowsableAttribute*/".ctor"); + + CheckAttributes(reader0, + new CustomAttributeRow(Handle(1, TableIndex.Assembly), Handle(1, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(1, TableIndex.Assembly), Handle(2, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(1, TableIndex.Assembly), Handle(3, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(2, TableIndex.MethodDef), Handle(5, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(2, TableIndex.TypeDef), Handle(4, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(3, TableIndex.TypeDef), Handle(4, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(4, TableIndex.MethodDef), Handle(5, TableIndex.MemberRef))); + + var generation0 = EmitBaseline.CreateInitialBaseline( + md0, + EmptyLocalsProvider); + var method1_1 = compilation1.GetMember("C.F"); + var method1_2 = compilation1.GetMember("D.A"); + + var diff1 = compilation1.EmitDifference( + generation0, + ImmutableArray.Create( + SemanticEdit.Create(SemanticEditKind.Update, method0_1, method1_1), + SemanticEdit.Create(SemanticEditKind.Update, method0_2, method1_2))); + + // Verify delta metadata contains expected rows. + using var md1 = diff1.GetMetadata(); + var reader1 = md1.Reader; + var readers = new[] { reader0, reader1 }; + + EncValidation.VerifyModuleMvid(1, reader0, reader1); + + CheckNames(readers, reader1.GetTypeDefNames()); + CheckNames(readers, reader1.GetMethodDefNames(), "F", "A"); + CheckNames(readers, reader1.GetMemberRefNames(), /*BrowsableAttribute*/".ctor", /*CategoryAttribute*/".ctor", /*DescriptionAttribute*/".ctor"); + + CheckAttributes(reader1, + new CustomAttributeRow(Handle(2, TableIndex.MethodDef), Handle(8, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(2, TableIndex.MethodDef), Handle(7, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(2, TableIndex.MethodDef), Handle(9, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(4, TableIndex.MethodDef), Handle(8, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(4, TableIndex.MethodDef), Handle(9, TableIndex.MemberRef))); + + CheckEncLog(reader1, + Row(2, TableIndex.AssemblyRef, EditAndContinueOperation.Default), + Row(7, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(8, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(9, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(8, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(9, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(10, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(11, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(2, TableIndex.StandAloneSig, EditAndContinueOperation.Default), + Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), // update existing row + Row(8, TableIndex.CustomAttribute, EditAndContinueOperation.Default), // add new row + Row(9, TableIndex.CustomAttribute, EditAndContinueOperation.Default), // add new row + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default), // update existing row + Row(10, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); // add new row + + CheckEncMap(reader1, + Handle(8, TableIndex.TypeRef), + Handle(9, TableIndex.TypeRef), + Handle(10, TableIndex.TypeRef), + Handle(11, TableIndex.TypeRef), + Handle(2, TableIndex.MethodDef), + Handle(4, TableIndex.MethodDef), + Handle(7, TableIndex.MemberRef), + Handle(8, TableIndex.MemberRef), + Handle(9, TableIndex.MemberRef), + Handle(4, TableIndex.CustomAttribute), + Handle(7, TableIndex.CustomAttribute), + Handle(8, TableIndex.CustomAttribute), + Handle(9, TableIndex.CustomAttribute), + Handle(10, TableIndex.CustomAttribute), + Handle(2, TableIndex.StandAloneSig), + Handle(2, TableIndex.AssemblyRef)); + } + + [Fact] + public void ModifyMethod_DeleteAttributes1() + { + var source0 = +@"class C +{ + static void Main() { } + [System.ComponentModel.Description(""The F method"")] + static string F() { return null; } +}"; + var source1 = +@"class C +{ + static void Main() { } + static string F() { return string.Empty; } +}"; + var source2 = +@"class C +{ + static void Main() { } + [System.ComponentModel.Description(""The F method"")] + static string F() { return string.Empty; } +}"; + + var compilation0 = CreateCompilation(source0, options: TestOptions.DebugExe, targetFramework: TargetFramework.NetStandard20); + var compilation1 = compilation0.WithSource(source1); + var compilation2 = compilation1.WithSource(source2); + + var method0 = compilation0.GetMember("C.F"); + + // Verify full metadata contains expected rows. + var bytes0 = compilation0.EmitToArray(); + using var md0 = ModuleMetadata.CreateFromImage(bytes0); + var reader0 = md0.MetadataReader; + + CheckNames(reader0, reader0.GetTypeDefNames(), "", "C"); + CheckNames(reader0, reader0.GetMethodDefNames(), "Main", "F", ".ctor"); + CheckNames(reader0, reader0.GetMemberRefNames(), /*CompilationRelaxationsAttribute.*/".ctor", /*RuntimeCompatibilityAttribute.*/".ctor", /*Object.*/".ctor", /*DebuggableAttribute*/".ctor", /*DescriptionAttribute*/".ctor"); + + CheckAttributes(reader0, + new CustomAttributeRow(Handle(1, TableIndex.Assembly), Handle(1, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(1, TableIndex.Assembly), Handle(2, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(1, TableIndex.Assembly), Handle(3, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(2, TableIndex.MethodDef), Handle(4, TableIndex.MemberRef))); + + var generation0 = EmitBaseline.CreateInitialBaseline( + md0, + EmptyLocalsProvider); + var method1 = compilation1.GetMember("C.F"); + + var diff1 = compilation1.EmitDifference( + generation0, + ImmutableArray.Create(SemanticEdit.Create(SemanticEditKind.Update, method0, method1))); + + // Verify delta metadata contains expected rows. + using var md1 = diff1.GetMetadata(); + var reader1 = md1.Reader; + var readers = new[] { reader0, reader1 }; + + EncValidation.VerifyModuleMvid(1, reader0, reader1); + + CheckNames(readers, reader1.GetTypeDefNames()); + CheckNames(readers, reader1.GetMethodDefNames(), "F"); + CheckNames(readers, reader1.GetMemberRefNames(), /*String.*/"Empty"); + + CheckAttributes(reader1, + new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef))); // Parent row id is 0, signifying a delete + + CheckEncLog(reader1, + Row(2, TableIndex.AssemblyRef, EditAndContinueOperation.Default), + Row(6, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(7, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(8, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(2, TableIndex.StandAloneSig, EditAndContinueOperation.Default), + Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); // Row 4, so updating existing CustomAttribute + + CheckEncMap(reader1, + Handle(7, TableIndex.TypeRef), + Handle(8, TableIndex.TypeRef), + Handle(2, TableIndex.MethodDef), + Handle(6, TableIndex.MemberRef), + Handle(4, TableIndex.CustomAttribute), + Handle(2, TableIndex.StandAloneSig), + Handle(2, TableIndex.AssemblyRef)); + + var method2 = compilation2.GetMember("C.F"); + var diff2 = compilation2.EmitDifference( + diff1.NextGeneration, + ImmutableArray.Create(SemanticEdit.Create(SemanticEditKind.Update, method1, method2))); + + // Verify delta metadata contains expected rows. + using var md2 = diff2.GetMetadata(); + var reader2 = md2.Reader; + readers = new[] { reader0, reader1, reader2 }; + + EncValidation.VerifyModuleMvid(2, reader1, reader2); + + CheckNames(readers, reader2.GetTypeDefNames()); + CheckNames(readers, reader2.GetMethodDefNames(), "F"); + CheckNames(readers, reader2.GetMemberRefNames(), /*DescriptionAttribute*/".ctor", /*String.*/"Empty"); + + CheckAttributes(reader2, + new CustomAttributeRow(Handle(2, TableIndex.MethodDef), Handle(7, TableIndex.MemberRef))); + + CheckEncLog(reader2, + Row(3, TableIndex.AssemblyRef, EditAndContinueOperation.Default), + Row(7, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(8, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(9, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(10, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(11, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(3, TableIndex.StandAloneSig, EditAndContinueOperation.Default), + Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); // Row 4, updating the original row back to a real one + + CheckEncMap(reader2, + Handle(9, TableIndex.TypeRef), + Handle(10, TableIndex.TypeRef), + Handle(11, TableIndex.TypeRef), + Handle(2, TableIndex.MethodDef), + Handle(7, TableIndex.MemberRef), + Handle(8, TableIndex.MemberRef), + Handle(4, TableIndex.CustomAttribute), + Handle(3, TableIndex.StandAloneSig), + Handle(3, TableIndex.AssemblyRef)); + } + + [Fact] + public void ModifyMethod_DeleteAttributes2() + { + var source0 = +@"class C +{ + static void Main() { } + static string F() { return null; } +}"; + var source1 = +@"class C +{ + static void Main() { } + [System.ComponentModel.Description(""The F method"")] + static string F() { return string.Empty; } +}"; + var source2 = source0; // Remove the attribute we just added + var source3 = source1; // Add the attribute back again + + var compilation0 = CreateCompilation(source0, options: TestOptions.DebugExe, targetFramework: TargetFramework.NetStandard20); + var compilation1 = compilation0.WithSource(source1); + var compilation2 = compilation1.WithSource(source2); + var compilation3 = compilation1.WithSource(source3); + + var method0 = compilation0.GetMember("C.F"); + + // Verify full metadata contains expected rows. + var bytes0 = compilation0.EmitToArray(); + using var md0 = ModuleMetadata.CreateFromImage(bytes0); + var reader0 = md0.MetadataReader; + + CheckNames(reader0, reader0.GetTypeDefNames(), "", "C"); + CheckNames(reader0, reader0.GetMethodDefNames(), "Main", "F", ".ctor"); + CheckNames(reader0, reader0.GetMemberRefNames(), /*CompilationRelaxationsAttribute.*/".ctor", /*RuntimeCompatibilityAttribute.*/".ctor", /*Object.*/".ctor", /*DebuggableAttribute*/".ctor"); + + CheckAttributes(reader0, + new CustomAttributeRow(Handle(1, TableIndex.Assembly), Handle(1, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(1, TableIndex.Assembly), Handle(2, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(1, TableIndex.Assembly), Handle(3, TableIndex.MemberRef))); + + var generation0 = EmitBaseline.CreateInitialBaseline( + md0, + EmptyLocalsProvider); + var method1 = compilation1.GetMember("C.F"); + + var diff1 = compilation1.EmitDifference( + generation0, + ImmutableArray.Create(SemanticEdit.Create(SemanticEditKind.Update, method0, method1))); + + // Verify delta metadata contains expected rows. + using var md1 = diff1.GetMetadata(); + var reader1 = md1.Reader; + var readers = new[] { reader0, reader1 }; + + EncValidation.VerifyModuleMvid(1, reader0, reader1); + + CheckNames(readers, reader1.GetTypeDefNames()); + CheckNames(readers, reader1.GetMethodDefNames(), "F"); + CheckNames(readers, reader1.GetMemberRefNames(), /*DescriptionAttribute*/".ctor", /*String.*/"Empty"); + + CheckAttributes(reader1, + new CustomAttributeRow(Handle(2, TableIndex.MethodDef), Handle(5, TableIndex.MemberRef))); + + CheckEncLog(reader1, + Row(2, TableIndex.AssemblyRef, EditAndContinueOperation.Default), + Row(5, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(6, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(6, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(7, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(8, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(2, TableIndex.StandAloneSig, EditAndContinueOperation.Default), + Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); // Row 4, so adding a new CustomAttribute + + CheckEncMap(reader1, + Handle(6, TableIndex.TypeRef), + Handle(7, TableIndex.TypeRef), + Handle(8, TableIndex.TypeRef), + Handle(2, TableIndex.MethodDef), + Handle(5, TableIndex.MemberRef), + Handle(6, TableIndex.MemberRef), + Handle(4, TableIndex.CustomAttribute), + Handle(2, TableIndex.StandAloneSig), + Handle(2, TableIndex.AssemblyRef)); + + var method2 = compilation2.GetMember("C.F"); + var diff2 = compilation2.EmitDifference( + diff1.NextGeneration, + ImmutableArray.Create( + SemanticEdit.Create(SemanticEditKind.Update, method1, method2))); + + // Verify delta metadata contains expected rows. + using var md2 = diff2.GetMetadata(); + var reader2 = md2.Reader; + readers = new[] { reader0, reader1, reader2 }; + + CheckNames(readers, reader2.GetTypeDefNames()); + CheckNames(readers, reader2.GetMethodDefNames(), "F"); + CheckNames(readers, reader2.GetMemberRefNames()); + + CheckAttributes(reader2, + new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef))); // 0, delete + + CheckEncLog(reader2, + Row(3, TableIndex.AssemblyRef, EditAndContinueOperation.Default), + Row(9, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(3, TableIndex.StandAloneSig, EditAndContinueOperation.Default), + Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); // Row 4, so updating existing CustomAttribute + + CheckEncMap(reader2, + Handle(9, TableIndex.TypeRef), + Handle(2, TableIndex.MethodDef), + Handle(4, TableIndex.CustomAttribute), + Handle(3, TableIndex.StandAloneSig), + Handle(3, TableIndex.AssemblyRef)); + + var method3 = compilation3.GetMember("C.F"); + var diff3 = compilation3.EmitDifference( + diff2.NextGeneration, + ImmutableArray.Create( + SemanticEdit.Create(SemanticEditKind.Update, method2, method3))); + + // Verify delta metadata contains expected rows. + using var md3 = diff3.GetMetadata(); + var reader3 = md3.Reader; + readers = new[] { reader0, reader1, reader2, reader3 }; + + CheckNames(readers, reader3.GetTypeDefNames()); + CheckNames(readers, reader3.GetMethodDefNames(), "F"); + CheckNames(readers, reader3.GetMemberRefNames(), /*DescriptionAttribute*/".ctor", /*String.*/"Empty"); + + CheckAttributes(reader3, + new CustomAttributeRow(Handle(2, TableIndex.MethodDef), Handle(7, TableIndex.MemberRef))); + + CheckEncLog(reader3, + Row(4, TableIndex.AssemblyRef, EditAndContinueOperation.Default), + Row(7, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(8, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(10, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(11, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(12, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(4, TableIndex.StandAloneSig, EditAndContinueOperation.Default), + Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); // Row 4, update the previously deleted row + + CheckEncMap(reader3, + Handle(10, TableIndex.TypeRef), + Handle(11, TableIndex.TypeRef), + Handle(12, TableIndex.TypeRef), + Handle(2, TableIndex.MethodDef), + Handle(7, TableIndex.MemberRef), + Handle(8, TableIndex.MemberRef), + Handle(4, TableIndex.CustomAttribute), + Handle(4, TableIndex.StandAloneSig), + Handle(4, TableIndex.AssemblyRef)); + } + + [Fact] + public void AddMethod_WithAttributes() + { + var source0 = +@"class C +{ + static void Main() { } +}"; + var source1 = +@"class C +{ + static void Main() { } + [System.ComponentModel.Description(""The F method"")] + static string F() { return string.Empty; } +}"; + var compilation0 = CreateCompilation(source0, options: TestOptions.DebugExe, targetFramework: TargetFramework.NetStandard20); + var compilation1 = compilation0.WithSource(source1); + + // Verify full metadata contains expected rows. + var bytes0 = compilation0.EmitToArray(); + using var md0 = ModuleMetadata.CreateFromImage(bytes0); + var reader0 = md0.MetadataReader; + + CheckNames(reader0, reader0.GetTypeDefNames(), "", "C"); + CheckNames(reader0, reader0.GetMethodDefNames(), "Main", ".ctor"); + CheckNames(reader0, reader0.GetMemberRefNames(), /*CompilationRelaxationsAttribute.*/".ctor", /*RuntimeCompatibilityAttribute.*/".ctor", /*Object.*/".ctor", /*DebuggableAttribute*/".ctor"); + + Assert.Equal(3, reader0.CustomAttributes.Count); + + var generation0 = EmitBaseline.CreateInitialBaseline( + md0, + EmptyLocalsProvider); + var method1 = compilation1.GetMember("C.F"); + + var diff1 = compilation1.EmitDifference( + generation0, + ImmutableArray.Create(SemanticEdit.Create(SemanticEditKind.Insert, null, method1))); + + // Verify delta metadata contains expected rows. + using var md1 = diff1.GetMetadata(); + var reader1 = md1.Reader; + var readers = new[] { reader0, reader1 }; + + EncValidation.VerifyModuleMvid(1, reader0, reader1); + + CheckNames(readers, reader1.GetTypeDefNames()); + CheckNames(readers, reader1.GetMethodDefNames(), "F"); + CheckNames(readers, reader1.GetMemberRefNames(), /*DescriptionAttribute*/".ctor", /*String.*/"Empty"); + + Assert.Equal(1, reader1.CustomAttributes.Count); + + CheckEncLog(reader1, + Row(2, TableIndex.AssemblyRef, EditAndContinueOperation.Default), + Row(5, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(6, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(6, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(7, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(8, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(1, TableIndex.StandAloneSig, EditAndContinueOperation.Default), + Row(2, TableIndex.TypeDef, EditAndContinueOperation.AddMethod), + Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default)); // Row 4, a new attribute + + CheckEncMap(reader1, + Handle(6, TableIndex.TypeRef), + Handle(7, TableIndex.TypeRef), + Handle(8, TableIndex.TypeRef), + Handle(3, TableIndex.MethodDef), + Handle(5, TableIndex.MemberRef), + Handle(6, TableIndex.MemberRef), + Handle(4, TableIndex.CustomAttribute), + Handle(1, TableIndex.StandAloneSig), + Handle(2, TableIndex.AssemblyRef)); + } + [WorkItem(962219, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/962219")] [Fact] public void PartialMethod() @@ -2266,6 +2952,7 @@ [B] static void M2<[A]T>() { } Row(8, TableIndex.Param, EditAndContinueOperation.Default), Row(15, TableIndex.MethodDef, EditAndContinueOperation.AddParameter), Row(9, TableIndex.Param, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default), Row(13, TableIndex.CustomAttribute, EditAndContinueOperation.Default), Row(14, TableIndex.CustomAttribute, EditAndContinueOperation.Default), Row(15, TableIndex.CustomAttribute, EditAndContinueOperation.Default), @@ -2274,7 +2961,6 @@ [B] static void M2<[A]T>() { } Row(18, TableIndex.CustomAttribute, EditAndContinueOperation.Default), Row(19, TableIndex.CustomAttribute, EditAndContinueOperation.Default), Row(20, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(21, TableIndex.CustomAttribute, EditAndContinueOperation.Default), Row(4, TableIndex.MethodSemantics, EditAndContinueOperation.Default), Row(5, TableIndex.MethodSemantics, EditAndContinueOperation.Default), Row(6, TableIndex.MethodSemantics, EditAndContinueOperation.Default), @@ -2300,6 +2986,7 @@ [B] static void M2<[A]T>() { } Handle(13, TableIndex.MemberRef), Handle(14, TableIndex.MemberRef), Handle(15, TableIndex.MemberRef), + Handle(7, TableIndex.CustomAttribute), Handle(13, TableIndex.CustomAttribute), Handle(14, TableIndex.CustomAttribute), Handle(15, TableIndex.CustomAttribute), @@ -2308,7 +2995,6 @@ [B] static void M2<[A]T>() { } Handle(18, TableIndex.CustomAttribute), Handle(19, TableIndex.CustomAttribute), Handle(20, TableIndex.CustomAttribute), - Handle(21, TableIndex.CustomAttribute), Handle(3, TableIndex.StandAloneSig), Handle(4, TableIndex.StandAloneSig), Handle(2, TableIndex.Event), diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs index 353c61b7f2e88..46b3955fa274d 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs @@ -38,6 +38,18 @@ internal sealed class DeltaMetadataWriter : MetadataWriter private readonly EventOrPropertyMapIndex _propertyMap; private readonly MethodImplIndex _methodImpls; + // For the EncLog table we need to know which things we're emitting custom attributes for so we can + // correctly map the attributes to row numbers of existing attributes for that target + private readonly Dictionary _customAttributeParentCounts; + + // For the EncMap table we need to keep a list of exactly which rows in the CustomAttributes table are updated + // since we spread these out amongst existing rows, it's not just a contiguous set + private readonly List _customAttributeEncMapRows; + + // Keep track of which CustomAttributes rows are added in this and previous deltas, over what is in the + // original metadata + private readonly Dictionary> _customAttributesAdded; + private readonly HeapOrReferenceIndex _assemblyRefIndex; private readonly HeapOrReferenceIndex _moduleRefIndex; private readonly InstanceAndStructuralReferenceIndex _memberRefIndex; @@ -90,6 +102,10 @@ public DeltaMetadataWriter( _propertyMap = new EventOrPropertyMapIndex(this.TryGetExistingPropertyMapIndex, sizes[(int)TableIndex.PropertyMap]); _methodImpls = new MethodImplIndex(this, sizes[(int)TableIndex.MethodImpl]); + _customAttributeParentCounts = new Dictionary(); + _customAttributeEncMapRows = new List(); + _customAttributesAdded = new Dictionary>(); + _assemblyRefIndex = new HeapOrReferenceIndex(this, lastRowId: sizes[(int)TableIndex.AssemblyRef]); _moduleRefIndex = new HeapOrReferenceIndex(this, lastRowId: sizes[(int)TableIndex.ModuleRef]); _memberRefIndex = new InstanceAndStructuralReferenceIndex(this, new MemberRefComparer(this), lastRowId: sizes[(int)TableIndex.MemberRef]); @@ -171,6 +187,7 @@ internal EmitBaseline GetDelta(EmitBaseline baseline, Compilation compilation, G eventMapAdded: AddRange(_previousGeneration.EventMapAdded, _eventMap.GetAdded()), propertyMapAdded: AddRange(_previousGeneration.PropertyMapAdded, _propertyMap.GetAdded()), methodImplsAdded: AddRange(_previousGeneration.MethodImplsAdded, _methodImpls.GetAdded()), + customAttributesAdded: AddRange(_previousGeneration.CustomAttributesAdded, _customAttributesAdded, replace: true), tableEntriesAdded: ImmutableArray.Create(tableSizes), // Blob stream is concatenated aligned. blobStreamLengthAdded: metadataSizes.GetAlignedHeapSize(HeapIndex.Blob) + _previousGeneration.BlobStreamLengthAdded, @@ -692,6 +709,17 @@ private EncLocalInfo CreateEncLocalInfo(ILocalDefinition localDef, byte[] signat return new EncLocalInfo(localDef.SlotInfo, translatedType, localDef.Constraints, signature); } + protected override int AddCustomAttributesToTable(EntityHandle parentHandle, IEnumerable attributes) + { + // The base class will write out the actual metadata for us + var numAttributesEmitted = base.AddCustomAttributesToTable(parentHandle, attributes); + + // We need to keep track of all of the things attributes could be associated with in this delta, in order to populate the EncLog and Map tables + _customAttributeParentCounts.Add(parentHandle, numAttributesEmitted); + + return numAttributesEmitted; + } + protected override void PopulateEncLogTableRows(ImmutableArray rowCounts) { // The EncLog table is a log of all the operations needed @@ -720,7 +748,7 @@ protected override void PopulateEncLogTableRows(ImmutableArray rowCounts) PopulateEncLogTableParameters(); PopulateEncLogTableRows(TableIndex.Constant, previousSizes, deltaSizes); - PopulateEncLogTableRows(TableIndex.CustomAttribute, previousSizes, deltaSizes); + PopulateEncLogTableCustomAttributes(); PopulateEncLogTableRows(TableIndex.DeclSecurity, previousSizes, deltaSizes); PopulateEncLogTableRows(TableIndex.ClassLayout, previousSizes, deltaSizes); PopulateEncLogTableRows(TableIndex.FieldLayout, previousSizes, deltaSizes); @@ -802,6 +830,118 @@ private void PopulateEncLogTableParameters() } } + /// + /// CustomAttributes point to their target via the Parent column so we cannot simply output new rows + /// in the delta or we would end up with duplicates, but we also don't want to do complex logic to determine + /// which attributes have changes, so we just emit them all. + /// This means our logic for emitting CustomAttributes is to update any existing rows, either from the original + /// compilation or subsequent deltas, and only add more if we need to. The EncLog table is the thing that tells + /// the runtime which row a CustomAttributes row is (ie, new or existing) + /// + private void PopulateEncLogTableCustomAttributes() + { + // List of attributes that need to be emitted to delete a previously emitted attribute + var deletedAttributeRows = new List<(int parentRowId, HandleKind kind)>(); + var customAttributesAdded = new Dictionary>(); + + // The data in _previousGeneration.CustomAttributesAdded is not nicely sorted, or even necessarily contiguous + // so we need to map each target onto the rows its attributes occupy so we know which rows to update + var lastRowId = _previousGeneration.OriginalMetadata.MetadataReader.GetTableRowCount(TableIndex.CustomAttribute); + if (_previousGeneration.CustomAttributesAdded.Count > 0) + { + lastRowId = _previousGeneration.CustomAttributesAdded.SelectMany(s => s.Value).Max(); + } + + // Iterate through the parents we emitted custom attributes for, in parent order + foreach (var (parent, count) in _customAttributeParentCounts.OrderBy(kvp => CodedIndex.HasCustomAttribute(kvp.Key))) + { + int index = 0; + + // First we try to update any existing attributes. + // GetCustomAttributes does a binary search, so is fast. We presume that the number of rows in the original metadata + // greatly outnumbers the amount of parents emitted in this delta so even with repeated searches this is still + // quicker than iterating the entire original table, even once. + var existingCustomAttributes = _previousGeneration.OriginalMetadata.MetadataReader.GetCustomAttributes(parent); + foreach (var attributeHandle in existingCustomAttributes) + { + int rowId = MetadataTokens.GetRowNumber(attributeHandle); + AddLogEntryOrDelete(rowId, parent, add: index < count); + index++; + } + + // If we emitted any attributes for this parent in previous deltas then we either need to update + // them next, or delete them if necessary + if (_previousGeneration.CustomAttributesAdded.TryGetValue(parent, out var rowIds)) + { + foreach (var rowId in rowIds) + { + TrackCustomAttributeAdded(rowId, parent); + AddLogEntryOrDelete(rowId, parent, add: index < count); + index++; + } + } + + // Finally if there are still attributes for this parent left, they are additions new to this delta + for (int i = index; i < count; i++) + { + lastRowId++; + TrackCustomAttributeAdded(lastRowId, parent); + AddEncLogEntry(lastRowId); + } + } + + // Save the attributes we've emitted, and the ones from previous deltas, for use in the next generation + foreach (var (parent, rowIds) in customAttributesAdded) + { + _customAttributesAdded.Add(parent, rowIds.ToImmutableAndFree()); + } + + // Add attributes and log entries for everything we've deleted + foreach (var row in deletedAttributeRows) + { + // now emit a "delete" row with a parent that is for the 0 row of the same table as the existing one + if (!MetadataTokens.TryGetTableIndex(row.kind, out var tableIndex)) + { + throw new InvalidOperationException("Trying to delete a custom attribute for a parent kind that doesn't have a matching table index."); + } + metadata.AddCustomAttribute(MetadataTokens.Handle(tableIndex, 0), MetadataTokens.EntityHandle(TableIndex.MemberRef, 0), value: default); + + AddEncLogEntry(row.parentRowId); + } + + void AddEncLogEntry(int rowId) + { + _customAttributeEncMapRows.Add(rowId); + metadata.AddEncLogEntry( + entity: MetadataTokens.CustomAttributeHandle(rowId), + code: EditAndContinueOperation.Default); + } + + void AddLogEntryOrDelete(int rowId, EntityHandle parent, bool add) + { + if (add) + { + // Update this row + AddEncLogEntry(rowId); + } + else + { + // Delete this row + deletedAttributeRows.Add((rowId, parent.Kind)); + } + } + + void TrackCustomAttributeAdded(int nextRowId, EntityHandle parent) + { + if (!customAttributesAdded.TryGetValue(parent, out var existing)) + { + existing = ArrayBuilder.GetInstance(); + customAttributesAdded.Add(parent, existing); + } + existing.Add(nextRowId); + } + } + private void PopulateEncLogTableRows(DefinitionIndex index, TableIndex tableIndex) where T : class, IDefinition { @@ -854,7 +994,7 @@ protected override void PopulateEncMapTableRows(ImmutableArray rowCounts) AddReferencedTokens(tokens, TableIndex.Param, previousSizes, deltaSizes); AddReferencedTokens(tokens, TableIndex.Constant, previousSizes, deltaSizes); - AddReferencedTokens(tokens, TableIndex.CustomAttribute, previousSizes, deltaSizes); + AddRowNumberTokens(tokens, _customAttributeEncMapRows, TableIndex.CustomAttribute); AddReferencedTokens(tokens, TableIndex.DeclSecurity, previousSizes, deltaSizes); AddReferencedTokens(tokens, TableIndex.ClassLayout, previousSizes, deltaSizes); AddReferencedTokens(tokens, TableIndex.FieldLayout, previousSizes, deltaSizes); @@ -981,6 +1121,14 @@ private static void AddDefinitionTokens(ArrayBuilder tokens, De } } + private static void AddRowNumberTokens(ArrayBuilder tokens, IEnumerable rowNumbers, TableIndex tableIndex) + { + foreach (var row in rowNumbers) + { + tokens.Add(MetadataTokens.Handle(tableIndex, row)); + } + } + protected override void PopulateEventMapTableRows() { foreach (var typeId in _eventMap.GetRows()) diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/EmitBaseline.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/EmitBaseline.cs index b5e2eec851b7e..b16628e10f05b 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/EmitBaseline.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/EmitBaseline.cs @@ -212,6 +212,7 @@ public static EmitBaseline CreateInitialBaseline( eventMapAdded: new Dictionary(), propertyMapAdded: new Dictionary(), methodImplsAdded: new Dictionary(), + customAttributesAdded: new Dictionary>(), tableEntriesAdded: s_emptyTableSizes, blobStreamLengthAdded: 0, stringStreamLengthAdded: 0, @@ -262,6 +263,7 @@ public static EmitBaseline CreateInitialBaseline( internal readonly IReadOnlyDictionary EventMapAdded; internal readonly IReadOnlyDictionary PropertyMapAdded; internal readonly IReadOnlyDictionary MethodImplsAdded; + internal readonly IReadOnlyDictionary> CustomAttributesAdded; internal readonly ImmutableArray TableEntriesAdded; @@ -317,6 +319,7 @@ private EmitBaseline( IReadOnlyDictionary eventMapAdded, IReadOnlyDictionary propertyMapAdded, IReadOnlyDictionary methodImplsAdded, + IReadOnlyDictionary> customAttributesAdded, ImmutableArray tableEntriesAdded, int blobStreamLengthAdded, int stringStreamLengthAdded, @@ -377,6 +380,7 @@ private EmitBaseline( EventMapAdded = eventMapAdded; PropertyMapAdded = propertyMapAdded; MethodImplsAdded = methodImplsAdded; + CustomAttributesAdded = customAttributesAdded; TableEntriesAdded = tableEntriesAdded; BlobStreamLengthAdded = blobStreamLengthAdded; StringStreamLengthAdded = stringStreamLengthAdded; @@ -407,6 +411,7 @@ internal EmitBaseline With( IReadOnlyDictionary eventMapAdded, IReadOnlyDictionary propertyMapAdded, IReadOnlyDictionary methodImplsAdded, + IReadOnlyDictionary> customAttributesAdded, ImmutableArray tableEntriesAdded, int blobStreamLengthAdded, int stringStreamLengthAdded, @@ -438,6 +443,7 @@ internal EmitBaseline With( eventMapAdded, propertyMapAdded, methodImplsAdded, + customAttributesAdded, tableEntriesAdded, blobStreamLengthAdded: blobStreamLengthAdded, stringStreamLengthAdded: stringStreamLengthAdded, diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/SymbolMatcher.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/SymbolMatcher.cs index c30390aee8bc9..890c0699ec78d 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/SymbolMatcher.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/SymbolMatcher.cs @@ -51,6 +51,7 @@ public EmitBaseline MapBaselineToCompilation( eventMapAdded: baseline.EventMapAdded, propertyMapAdded: baseline.PropertyMapAdded, methodImplsAdded: baseline.MethodImplsAdded, + customAttributesAdded: baseline.CustomAttributesAdded, tableEntriesAdded: baseline.TableEntriesAdded, blobStreamLengthAdded: baseline.BlobStreamLengthAdded, stringStreamLengthAdded: baseline.StringStreamLengthAdded, diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index 96e49d321b22f..24ba7a0146f1b 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -1158,7 +1158,6 @@ private BlobHandle GetMethodSignatureHandleAndBlob(IMethodReference methodRefere SerializeReturnValueAndParameters(encoder, methodReference, methodReference.ExtraParameters); - signatureBlob = builder.ToImmutableArray(); result = metadata.GetOrAddBlob(signatureBlob); _signatureIndex.Add(methodReference, KeyValuePairUtil.Create(result, signatureBlob)); @@ -2091,10 +2090,7 @@ private TypeReferenceHandle GetDummyAssemblyAttributeParent(bool isSecurity, boo private void AddModuleAttributesToTable(CommonPEModuleBuilder module) { Debug.Assert(this.IsFullMetadata); - foreach (ICustomAttribute customAttribute in module.GetSourceModuleAttributes()) - { - AddCustomAttributeToTable(EntityHandle.ModuleDefinition, customAttribute); - } + AddCustomAttributesToTable(EntityHandle.ModuleDefinition, module.GetSourceModuleAttributes()); } private void AddCustomAttributesToTable(IEnumerable parentList, TableIndex tableIndex) @@ -2104,10 +2100,7 @@ private void AddCustomAttributesToTable(IEnumerable parentList, TableIndex foreach (var parent in parentList) { var parentHandle = MetadataTokens.Handle(tableIndex, parentRowId++); - foreach (ICustomAttribute customAttribute in parent.GetAttributes(Context)) - { - AddCustomAttributeToTable(parentHandle, customAttribute); - } + AddCustomAttributesToTable(parentHandle, parent.GetAttributes(Context)); } } @@ -2117,33 +2110,19 @@ private void AddCustomAttributesToTable(IEnumerable parentList, Func attributes) + protected virtual int AddCustomAttributesToTable(EntityHandle parentHandle, IEnumerable attributes) { + int count = 0; foreach (var attr in attributes) { - AddCustomAttributeToTable(handle, attr); - } - } - - private void AddCustomAttributesToTable(IEnumerable typeRefsWithAttributes) - { - foreach (var typeRefWithAttributes in typeRefsWithAttributes) - { - var ifaceHandle = GetTypeHandle(typeRefWithAttributes.TypeRef); - foreach (var customAttribute in typeRefWithAttributes.Attributes) - { - AddCustomAttributeToTable(ifaceHandle, customAttribute); - } + count++; + AddCustomAttributeToTable(parentHandle, attr); } + return count; } private void AddCustomAttributeToTable(EntityHandle parentHandle, ICustomAttribute customAttribute) @@ -2435,7 +2414,6 @@ private void PopulateFileTableRows() } } - private void PopulateGenericParameters( ImmutableArray sortedGenericParameters) { diff --git a/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.vb b/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.vb index 1e1e3abbe0b7d..d1a70a3446e3e 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.vb @@ -222,7 +222,7 @@ End Class Row(4, TableIndex.StandAloneSig, EditAndContinueOperation.Default), Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) End Sub @@ -435,9 +435,9 @@ End Module Row(7, TableIndex.StandAloneSig, EditAndContinueOperation.Default), Row(6, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(11, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(18, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(19, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(20, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(9, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(10, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(14, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) End Sub diff --git a/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.vb b/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.vb index 49aca9d89d25c..fc4bd161e65f5 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.vb @@ -911,7 +911,8 @@ End Class Using md1 = diff1.GetMetadata() CheckEncLogDefinitions(md1.Reader, Row(3, TableIndex.StandAloneSig, EditAndContinueOperation.Default), - Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default)) + Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) End Using End Using End Sub @@ -958,7 +959,9 @@ End Class Using md1 = diff1.GetMetadata() CheckEncLogDefinitions(md1.Reader, Row(3, TableIndex.StandAloneSig, EditAndContinueOperation.Default), - Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default)) + Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) End Using End Using End Sub @@ -1038,15 +1041,15 @@ End Class Row(6, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(9, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(12, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(19, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(20, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(21, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(22, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(23, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(24, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(25, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(26, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(27, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(9, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(10, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(13, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(15, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(17, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) End Using End Using End Sub @@ -1101,9 +1104,9 @@ End Class Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(5, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(13, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(14, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(15, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) End Using diff1.VerifyIL("C.VB$StateMachine_1_F.MoveNext()", " @@ -1250,9 +1253,9 @@ End Class Row(4, TableIndex.StandAloneSig, EditAndContinueOperation.Default), Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(9, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(10, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(11, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) End Using diff1.VerifyIL("C.VB$StateMachine_1_F.MoveNext()", " @@ -1500,9 +1503,9 @@ End Class Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(5, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(13, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(14, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(15, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) End Using diff1.VerifyIL("C.VB$StateMachine_1_F.MoveNext()", " @@ -1614,9 +1617,9 @@ End Class Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(5, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(13, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(14, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(15, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) End Using diff1.VerifyIL("C.VB$StateMachine_1_F.MoveNext()", " @@ -1731,9 +1734,9 @@ End Class Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(5, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(13, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(14, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(15, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) End Using diff1.VerifyIL("C.VB$StateMachine_1_F.MoveNext()", " @@ -1844,9 +1847,9 @@ End Class Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(5, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(13, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(14, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(15, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) End Using diff1.VerifyIL("C.VB$StateMachine_1_F.MoveNext()", " @@ -1987,9 +1990,9 @@ End Class Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(5, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(13, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(14, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(15, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) End Using diff1.VerifyIL("C.VB$StateMachine_1_F.MoveNext()", " @@ -2134,9 +2137,9 @@ End Class Row(4, TableIndex.StandAloneSig, EditAndContinueOperation.Default), Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(9, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(10, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(11, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) End Using diff1.VerifyIL("C.VB$StateMachine_1_F.MoveNext()", " @@ -2296,9 +2299,9 @@ End Class Row(7, TableIndex.Field, EditAndContinueOperation.Default), Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(9, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(10, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(11, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) End Using diff1.VerifyIL("C.VB$StateMachine_1_F.MoveNext()", " @@ -2461,9 +2464,9 @@ End Class Row(7, TableIndex.Field, EditAndContinueOperation.Default), Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(9, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(10, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(11, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) End Using diff1.VerifyIL("C.VB$StateMachine_1_F.MoveNext()", " @@ -2622,9 +2625,9 @@ End Class Row(6, TableIndex.Field, EditAndContinueOperation.Default), Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(9, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(10, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(11, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) End Using diff1.VerifyIL("C.VB$StateMachine_1_F.MoveNext()", " @@ -2930,12 +2933,12 @@ End Class Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(6, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(9, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(19, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(20, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(21, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(22, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(23, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(24, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(13, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(15, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) diff1.VerifyIL("C.VB$StateMachine_1_F.MoveNext()", " { @@ -3048,9 +3051,9 @@ End Class Row(21, TableIndex.Field, EditAndContinueOperation.Default), Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(6, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(25, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(26, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(27, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(13, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) diff2.VerifyIL("C.VB$StateMachine_1_F.MoveNext()", " { @@ -3167,12 +3170,12 @@ End Class Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(9, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(12, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(28, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(29, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(30, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(31, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(32, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(33, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(9, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(10, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(15, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(17, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) End Sub @@ -3444,12 +3447,12 @@ End Class Row(6, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(9, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(12, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(19, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(20, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(21, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(22, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(23, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(24, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(9, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(10, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(13, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(15, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) diff1.VerifyIL("C.VB$StateMachine_4_F.MoveNext()", " { @@ -3603,9 +3606,9 @@ End Class Row(15, TableIndex.Field, EditAndContinueOperation.Default), Row(5, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(9, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(25, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(26, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(27, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(13, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) diff2.VerifyIL("C.VB$StateMachine_4_F.MoveNext()", " { @@ -3765,12 +3768,12 @@ End Class Row(7, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(12, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(15, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(28, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(29, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(30, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(31, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(32, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(33, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) + Row(9, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(10, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(11, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(12, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(15, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(17, TableIndex.CustomAttribute, EditAndContinueOperation.Default)) End Sub From 07e319e19a214384b178f4d2741e1134d82dd702 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Wed, 16 Jun 2021 10:24:46 -0700 Subject: [PATCH 127/127] Update the static abstract interface test --- .../InheritanceMarginTests.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs index 963cbcbf9ce7a..da73cc155115a 100644 --- a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs +++ b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs @@ -1055,7 +1055,7 @@ public class {|target1:Class1|} : I1 targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "class Class1", locationTag: "target1", - relationship: InheritanceRelationship.Implemented))); + relationship: InheritanceRelationship.ImplementingType))); var itemForM1InI1 = new TestInheritanceMemberItem( lineNumber: 4, @@ -1063,7 +1063,7 @@ public class {|target1:Class1|} : I1 targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "static void Class1.M1()", locationTag: "target2", - relationship: InheritanceRelationship.Implemented))); + relationship: InheritanceRelationship.ImplementingMember))); var itemForAbsClass1 = new TestInheritanceMemberItem( lineNumber: 11, @@ -1071,7 +1071,7 @@ public class {|target1:Class1|} : I1 targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "interface I1", locationTag: "target5", - relationship: InheritanceRelationship.Implementing))); + relationship: InheritanceRelationship.ImplementedInterface))); var itemForM1InClass1 = new TestInheritanceMemberItem( lineNumber: 13, @@ -1079,7 +1079,7 @@ public class {|target1:Class1|} : I1 targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "void I1.M1()", locationTag: "target4", - relationship: InheritanceRelationship.Implementing))); + relationship: InheritanceRelationship.ImplementedMember))); var itemForP1InI1 = new TestInheritanceMemberItem( lineNumber: 5, @@ -1087,7 +1087,7 @@ public class {|target1:Class1|} : I1 targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "static int Class1.P1 { get; set; }", locationTag: "target6", - relationship: InheritanceRelationship.Implemented))); + relationship: InheritanceRelationship.ImplementingMember))); var itemForP1InClass1 = new TestInheritanceMemberItem( lineNumber: 14, @@ -1095,7 +1095,7 @@ public class {|target1:Class1|} : I1 targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "int I1.P1 { get; set; }", locationTag: "target7", - relationship: InheritanceRelationship.Implementing))); + relationship: InheritanceRelationship.ImplementedMember))); var itemForE1InI1 = new TestInheritanceMemberItem( lineNumber: 6, @@ -1103,7 +1103,7 @@ public class {|target1:Class1|} : I1 targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "static event EventHandler Class1.e1", locationTag: "target8", - relationship: InheritanceRelationship.Implemented))); + relationship: InheritanceRelationship.ImplementingMember))); var itemForE1InClass1 = new TestInheritanceMemberItem( lineNumber: 15, @@ -1111,7 +1111,7 @@ public class {|target1:Class1|} : I1 targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "event EventHandler I1.e1", locationTag: "target9", - relationship: InheritanceRelationship.Implementing))); + relationship: InheritanceRelationship.ImplementedMember))); var itemForPlusOperatorInI1 = new TestInheritanceMemberItem( lineNumber: 7, @@ -1119,7 +1119,7 @@ public class {|target1:Class1|} : I1 targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "static int Class1.operator +(Class1)", locationTag: "target10", - relationship: InheritanceRelationship.Implemented))); + relationship: InheritanceRelationship.ImplementingMember))); var itemForPlusOperatorInClass1 = new TestInheritanceMemberItem( lineNumber: 16, @@ -1127,7 +1127,7 @@ public class {|target1:Class1|} : I1 targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "int I1.operator +(T)", locationTag: "target11", - relationship: InheritanceRelationship.Implementing))); + relationship: InheritanceRelationship.ImplementedMember))); var itemForIntOperatorInI1 = new TestInheritanceMemberItem( lineNumber: 8, @@ -1135,7 +1135,7 @@ public class {|target1:Class1|} : I1 targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "static Class1.implicit operator int(Class1)", locationTag: "target13", - relationship: InheritanceRelationship.Implemented))); + relationship: InheritanceRelationship.ImplementingMember))); var itemForIntOperatorInClass1 = new TestInheritanceMemberItem( lineNumber: 17, @@ -1143,7 +1143,7 @@ public class {|target1:Class1|} : I1 targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "I1.implicit operator int(T)", locationTag: "target12", - relationship: InheritanceRelationship.Implementing))); + relationship: InheritanceRelationship.ImplementedMember))); return VerifyInSingleDocumentAsync( markup,