From 3e874d0c448306f8f5cee37ccfbfdd6835e1709f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 07:41:18 +0000 Subject: [PATCH 1/6] [release/10.0.1xx] Source code updates from dotnet/razor (#2422) [release/10.0.1xx] Source code updates from dotnet/razor --- .../src/SourceGenerators/RazorCohostingOptions.cs | 2 +- .../RazorSourceGeneratorTests.cs | 5 ----- src/source-manifest.json | 4 ++-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/razor/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorCohostingOptions.cs b/src/razor/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorCohostingOptions.cs index 21db8554f0a..fc1a8a78f1e 100644 --- a/src/razor/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorCohostingOptions.cs +++ b/src/razor/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorCohostingOptions.cs @@ -5,5 +5,5 @@ internal static class RazorCohostingOptions /// /// True if razor is running in the cohosting mode /// - internal static bool UseRazorCohostServer { get; set; } = true; + internal static bool UseRazorCohostServer { get; set; } = false; } diff --git a/src/razor/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTests.cs b/src/razor/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTests.cs index 8c78aaec0db..20e5cba815c 100644 --- a/src/razor/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTests.cs +++ b/src/razor/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTests.cs @@ -10,7 +10,6 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor.Language.Syntax; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Test.Utilities; @@ -2616,9 +2615,6 @@ public async Task SourceGenerator_DoesNotUpdateSources_WhenSourceGeneratorIsSupp // start with the generator suppressed (this is the default state in VS) driver = SetSuppressionState(true); - // Disable co-hosting, this test only applies to non-cohosting scenarios - RazorCohostingOptions.UseRazorCohostServer = false; - // results should be empty, and no recorded steps should have run using var eventListener = new RazorEventListener(); var result = RunGenerator(compilation!, ref driver).VerifyPageOutput(); @@ -3461,7 +3457,6 @@ public async Task UseRazorCohostServer_CanOverride_Suppression() ["Component.Razor"] = "

Hello world

", }); var compilation = await project.GetCompilationAsync(); - RazorCohostingOptions.UseRazorCohostServer = false; // Start with the generator suppressed var (driver, additionalTexts, optionsProvider) = await GetDriverWithAdditionalTextAndProviderAsync(project, configureGlobalOptions: (o) => diff --git a/src/source-manifest.json b/src/source-manifest.json index 0c9c5aae0e9..70a1b9c7245 100644 --- a/src/source-manifest.json +++ b/src/source-manifest.json @@ -67,10 +67,10 @@ "commitSha": "28aa7988271232f1feec583cd7e0144ac8f42ee4" }, { - "barId": 282857, + "barId": 283108, "path": "razor", "remoteUri": "https://github.com/dotnet/razor", - "commitSha": "34bcb6b4b58087a7f0f54293b5bdab30284eb66b" + "commitSha": "bde4f70c9560811a7f25023b9d8ac42fd7d0e99f" }, { "barId": 281892, From 76c90b64fb9d5277e43ec470223bf0892b823886 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 08:11:59 +0000 Subject: [PATCH 2/6] [release/10.0.1xx] Update dependencies from dotnet/arcade-services (#2424) [release/10.0.1xx] Update dependencies from dotnet/arcade-services --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 8d1ac163534..1460435f520 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "microsoft.dotnet.darc": { - "version": "1.1.0-beta.25454.3", + "version": "1.1.0-beta.25461.2", "commands": [ "darc" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index ff26f6b94ea..eeedaa8ddd0 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -10,9 +10,9 @@ https://github.com/dotnet/dotnet 2db1f5ee2bdda2e8d873769325fabede32e420e0 - + https://github.com/dotnet/arcade-services - 8a799cfe36a94b864726b9b9aa673bd65753e238 + 4aa8e8c35aaf3cbdfc387d28af3366fcd9125bc8 From b067b59875bd4e4764cf04360970b3dea36bb47e Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 14:09:17 +0000 Subject: [PATCH 3/6] [release/10.0.1xx] Source code updates from dotnet/roslyn (#2420) [release/10.0.1xx] Source code updates from dotnet/roslyn - Temporarily re-add ITypeSymbol extension APIs --- src/roslyn/azure-pipelines-official.yml | 5 +- src/roslyn/azure-pipelines.yml | 27 +- .../Compiler Breaking Changes - DotNet 10.md | 2 +- .../docs/contributing/Compiler Test Plan.md | 8 + src/roslyn/eng/Directory.Packages.props | 10 +- src/roslyn/eng/Version.Details.props | 8 +- src/roslyn/eng/Version.Details.xml | 18 +- src/roslyn/eng/Versions.props | 11 + src/roslyn/eng/build.sh | 1 + .../job/publish-build-assets.yml | 9 +- .../eng/common/core-templates/jobs/jobs.yml | 2 + .../core-templates/steps/generate-sbom.yml | 2 +- .../steps/source-index-stage1-publish.yml | 4 +- src/roslyn/global.json | 8 +- ...dAnonymousTypeMemberNameCodeFixProvider.cs | 3 +- .../MakeLocalFunctionStaticCodeFixHelper.cs | 3 +- .../UseExplicitTypeCodeFixProvider.cs | 2 +- .../GenerateConstructorTests.cs | 2 +- ...plementAbstractClassTests_ThroughMember.cs | 2 +- .../CSharpDeclareAsNullableCodeFixTests.cs | 2 +- .../SimplifyInterpolationTests.cs | 304 +- .../IDEDiagnosticIdToOptionMappingHelper.cs | 6 +- ...AttributeSuppressionsDiagnosticAnalyzer.cs | 2 +- ...SimplifyInterpolationDiagnosticAnalyzer.cs | 15 +- .../AbstractSimplifyInterpolationHelpers.cs | 65 +- ...bstractGenerateConstructorService.State.cs | 2 +- ...actGenerateEnumMemberService.CodeAction.cs | 3 +- ...arameterizedMemberService.SignatureInfo.cs | 4 +- ...enerateParameterizedMemberService.State.cs | 2 +- ...tractGenerateParameterizedMemberService.cs | 5 +- ...actSimplifyInterpolationCodeFixProvider.cs | 6 +- ...erBasedUserDiagnosticTest_OptionHelpers.cs | 12 +- ...tLanguageServices.MefHostExportProvider.cs | 8 +- src/roslyn/src/CodeStyle/Tools/Program.cs | 4 +- .../CSharp/Portable/Binder/Binder_Crefs.cs | 35 +- .../Portable/Binder/Binder_Invocation.cs | 17 +- .../Portable/Binder/ForEachLoopBinder.cs | 10 +- .../Portable/Binder/RefSafetyAnalysis.cs | 2 +- .../Binder/WithCrefTypeParametersBinder.cs | 1 + .../Portable/BoundTree/UnboundLambda.cs | 25 +- .../Portable/Compiler/ClsComplianceChecker.cs | 2 +- .../FlowAnalysis/DefiniteAssignment.cs | 2 + .../Portable/FlowAnalysis/NullableWalker.cs | 42 +- .../AsyncRewriter/RuntimeAsyncRewriter.cs | 183 +- ...TreeToDifferentEnclosingContextRewriter.cs | 35 +- .../Lowering/ExtensionMethodBodyRewriter.cs | 12 - .../LocalRewriter/LocalRewriter_Call.cs | 67 +- .../LocalRewriter_ForEachStatement.cs | 21 +- .../Lowering/MethodToClassRewriter.cs | 43 +- .../StateMachineRewriter/CapturedSymbol.cs | 9 +- .../MethodToStateMachineRewriter.cs | 290 +- .../RefInitializationHoister.cs | 304 + .../CSharp/Portable/Parser/LanguageParser.cs | 57 +- .../CSharp/Portable/Symbols/AssemblySymbol.cs | 15 +- .../Symbols/PublicModel/MethodSymbol.cs | 15 + .../Symbols/PublicModel/NamedTypeSymbol.cs | 6 + .../Symbols/Source/SourceConstructorSymbol.cs | 12 +- .../Source/SourceMemberContainerSymbol.cs | 5 +- .../SynthesizedBackingFieldSymbol.cs | 10 +- .../Portable/Syntax/SyntaxNormalizer.cs | 2 +- .../Test/Emit/CodeGen/CodeGenAsyncEHTests.cs | 1 + .../Emit/CodeGen/CodeGenAsyncIteratorTests.cs | 2 +- .../Emit/CodeGen/CodeGenAsyncLocalsTests.cs | 1 + .../Emit/CodeGen/CodeGenAsyncMainTests.cs | 2 +- .../CodeGenAsyncMethodBuilderOverrideTests.cs | 1 + .../Emit/CodeGen/CodeGenAsyncSpillTests.cs | 5497 +++++++++++------ .../Test/Emit/CodeGen/CodeGenAsyncTests.cs | 23 + .../Emit/CodeGen/CodeGenAwaitForeachTests.cs | 316 +- .../Emit/CodeGen/CodeGenAwaitUsingTests.cs | 2 +- .../CodeGenRefConditionalOperatorTests.cs | 37 +- .../Test/Emit/CodeGen/CodeGenRefLocalTests.cs | 70 + .../Test/Emit2/CodeGen/CodeGenCallTests.cs | 886 ++- ...CodeAnalysis.CSharp.Emit2.UnitTests.csproj | 3 +- .../Diagnostics/DiagnosticAnalyzerTests.cs | 199 + .../CSharp/Test/Emit3/FieldKeywordTests.cs | 124 + ...CodeAnalysis.CSharp.Emit3.UnitTests.csproj | 1 + .../Test/Emit3/RefStructInterfacesTests.cs | 398 ++ .../Test/Emit3/Semantics/ExtensionTests2.cs | 1415 ++++- .../Test/Emit3/Semantics/InlineArrayTests.cs | 149 +- .../Semantic/Semantics/InterpolationTests.cs | 131 + .../Compilation/GetSemanticInfoTests.cs | 36 + .../SemanticModelGetSemanticInfoTests.cs | 14 +- ...larationParsingTests_MissingIdentifiers.cs | 2811 +++++++++ .../Syntax/Parsing/ExtensionsParsingTests.cs | 563 +- .../Syntax/Syntax/SyntaxNormalizerTests.cs | 41 + .../Compilers/CSharp/csc/AnyCpu/csc.csproj | 2 +- .../DiagnosticLocalizationTests.cs | 20 +- .../Core/MSBuildTask/InteractiveCompiler.cs | 2 - .../Core/MSBuildTask/ManagedCompiler.cs | 1 - .../Core/MSBuildTask/ManagedToolTask.cs | 65 +- .../Core/MSBuildTaskTests/CscTests.cs | 6 +- .../Core/MSBuildTaskTests/IntegrationTests.cs | 22 +- .../MSBuildManagedToolTests.cs | 5 +- ....Build.Tasks.CodeAnalysis.UnitTests.csproj | 3 +- .../TestUtilities/IntegrationTestBase.cs | 106 +- .../TestUtilities/TaskTestUtil.cs | 4 +- .../DiagnosticAnalyzer/AnalysisScope.cs | 11 +- .../DiagnosticAnalyzer/AnalyzerDriver.cs | 59 +- ...yzerExecutor.AnalyzerDiagnosticReporter.cs | 22 +- .../DiagnosticAnalyzer/AnalyzerExecutor.cs | 243 +- .../DiagnosticAnalyzer/AnalyzerOptions.cs | 5 + .../AnalyzerOptionsExtensions.cs | 4 + .../CompilationWithAnalyzers.cs | 4 +- .../CompilationWithAnalyzersOptions.cs | 25 + .../DiagnosticStartAnalysisScope.cs | 10 + .../InternalUtilities/PlatformInformation.cs | 2 + .../InternalUtilities/StringExtensions.cs | 11 +- .../Core/Portable/PublicAPI.Unshipped.txt | 4 + .../Core/Portable/Symbols/IFieldSymbol.cs | 5 + .../Core/Portable/Symbols/IMethodSymbol.cs | 22 +- .../Core/Portable/Symbols/INamedTypeSymbol.cs | 11 + .../Core/Portable/Symbols/ITypeSymbol.cs | 9 +- ...ld.Tasks.CodeAnalysis.Sdk.UnitTests.csproj | 1 + .../Core/SdkTaskTests/SdkManagedToolTests.cs | 12 +- .../VBCSCompiler/AnyCpu/VBCSCompiler.csproj | 2 +- .../CompilerServerApiTest.cs | 5 +- .../Compilers/Shared/BuildServerConnection.cs | 143 +- .../src/Compilers/Shared/RuntimeHostInfo.cs | 41 +- .../Compilation/VisualBasicCompilation.vb | 4 +- .../Portable/Symbols/MethodSymbol.vb | 6 + .../Portable/Symbols/NamedTypeSymbol.vb | 11 + .../Portable/Symbols/TypeSymbol.vb | 6 +- .../SymbolDisplay/SymbolDisplayTests.vb | 4 +- .../VisualBasic/vbc/AnyCpu/vbc.csproj | 2 +- ...EventHookupCommandHandler_TabKeyCommand.cs | 2 +- .../SemanticClassifierTests_Regex.cs | 28 + .../AddUsingTests_ExtensionMethods.cs | 62 +- ...ateEqualsAndGetHashCodeFromMembersTests.cs | 4 +- .../InitializeMemberFromParameterTests.cs | 6 +- ...berFromPrimaryConstructorParameterTests.cs | 6 +- .../CSharpMoveStaticMembersTests.cs | 47 + .../CodeActions/PreviewExceptionTests.cs | 6 +- .../DiagnosticAnalyzerDriverTests.cs | 4 +- .../MockDiagnosticAnalyzerTests.cs | 2 +- .../EventHookupCommandHandlerTests.cs | 4 +- .../EventHookup/EventHookupTestState.cs | 2 +- .../Formatting/CodeCleanupTests.TestFixers.cs | 14 +- .../NavigateTo/InteractiveNavigateToTests.cs | 10 +- .../DiagnosticAnalyzerQuickInfoSourceTests.cs | 17 +- .../AttributeSignatureHelpProviderTests.cs | 70 +- ...orInitializerSignatureHelpProviderTests.cs | 62 +- ...essExpressionSignatureHelpProviderTests.cs | 48 +- ...tiallyWrittenSignatureHelpProviderTests.cs | 32 +- .../GenericNameSignatureHelpProviderTests.cs | 58 +- ...ionExpressionSignatureHelpProviderTests.cs | 4 +- .../EditAndContinueLanguageService.cs | 9 +- .../EditAndContinueLanguageServiceBridge.cs | 9 +- .../Editor/EditorLayerExtensionManager.cs | 4 +- .../Api/IVSTypeScriptFindUsagesContext.cs | 2 +- .../VSTypeScriptDebugLocationInfoWrapper.cs | 2 +- .../Core/Host/IPreviewPaneService.cs | 2 +- .../AbstractInlineRenameUndoManager.cs | 4 +- .../HighlightTags/RenameConflictTag.cs | 2 +- .../RenameFieldBackgroundAndBorderTag.cs | 2 +- .../HighlightTags/RenameFixupTag.cs | 2 +- ...lineRenameSession.OpenTextBufferManager.cs | 6 +- .../UI/InlineRenameAdornmentManager.cs | 3 +- .../IntelliSense/AsyncCompletion/FilterSet.cs | 2 +- .../AsyncCompletion/ItemManagerProvider.cs | 2 +- .../Interactive/InertClassifierProvider.cs | 2 +- .../DefaultNavigateToPreviewServiceFactory.cs | 2 +- .../Core/NavigateTo/NavigateToItemDisplay.cs | 4 +- .../NavigationBar/NavigationBarController.cs | 2 +- .../EditorNotificationServiceFactory.cs | 2 +- ...ameTrackingTaggerProvider.UndoPrimitive.cs | 2 +- .../HostWorkspaceServicesExtensions.cs | 8 +- .../SignatureHelp/Presentation/Signature.cs | 2 +- .../SplitCommentOptionsStorage.cs | 2 +- .../Copilot/FlavoredSuggestedAction.cs | 50 - .../FixAll/FixMultipleOccurrencesService.cs | 7 +- .../PreviewChangesCodeAction.cs | 81 +- .../PreviewChangesSuggestedAction.cs | 45 - .../RefineUsingCopilotCodeAction.cs | 151 +- .../RefineUsingCopilotSuggestedAction.cs | 70 - .../SuggestedActionPriorityProvider.cs | 51 - .../CodeFixSuggestedAction.cs | 52 - .../CodeRefactoringSuggestedAction.cs | 36 - ...rSuggestedAction.CaretPositionRestorer.cs} | 2 +- ...stedAction.cs => EditorSuggestedAction.cs} | 85 +- ...itorSuggestedActionForRefactorOrFixAll.cs} | 48 +- ...EditorSuggestedActionWithNestedActions.cs} | 48 +- ...EditorSuggestedActionWithNestedFlavors.cs} | 124 +- ...CodeFixSuggestedAction.FixAllCodeAction.cs | 19 - .../FixAllCodeFixSuggestedAction.cs | 46 - .../FixAllCodeRefactoringSuggestedAction.cs | 38 - .../Suggestions/SuggestedActionsSource.cs | 11 +- .../SuggestedActionsSourceProvider.cs | 6 +- .../SuggestedActionsSource_Async.cs | 125 +- .../Core/Utilities/NativeMethods.cs | 2 +- .../AbstractChangeSignatureTests.cs | 4 +- .../CodeActions/AbstractCodeActionTest.cs | 14 +- .../Diagnostics/AbstractUserDiagnosticTest.cs | 4 +- .../Test/CodeFixes/CodeFixServiceTests.cs | 38 +- .../DiagnosticAnalyzerServiceTests.cs | 164 +- .../DocCommentFormattingTests.cs | 4 +- .../EditAndContinueLanguageServiceTests.cs | 6 + .../DocCommentFormatterTests.cs | 4 +- .../RenameTracking/RenameTrackingTestState.cs | 2 +- .../Test/Utilities/BloomFilterTests.cs | 18 +- .../SymbolEquivalenceComparerTests.cs | 2 +- .../Test2/CodeFixes/CodeFixServiceTests.vb | 2 +- ...stractCrossLanguageUserDiagnosticTests.vb} | 6 +- ...ImportCrossLanguageUserDiagnosticTests.vb} | 4 +- ...erenceCrossLanguageUserDiagnosticTests.vb} | 4 +- ...alFileCrossLanguageUserDiagnosticTests.vb} | 4 +- .../Diagnostics/DiagnosticProviderTests.vb | 2 +- .../Diagnostics/DiagnosticServiceTests.vb | 16 +- ...eEventCrossLanguageUserDiagnosticTests.vb} | 4 +- ...ructorCrossLanguageUserDiagnosticTests.vb} | 4 +- ...MethodCrossLanguageUserDiagnosticTests.vb} | 4 +- ...riableCrossLanguageUserDiagnosticTests.vb} | 4 +- ...erfaceCrossLanguageUserDiagnosticTests.vb} | 4 +- ...opertyCrossLanguageUserDiagnosticTests.vb} | 4 +- .../FindReferences/FindReferencesTests.vb | 2 +- ...seTests.vb => VisualBasicGoToBaseTests.vb} | 0 ...arpCompletionCommandHandlerTests_Await.vb} | 0 ... => CompletionServiceTests_Exclusivity.vb} | 0 ...sicCompletionCommandHandlerTests_Await.vb} | 0 ...onTests.vb => ModuleNameSimplifierTest.vb} | 0 ...icationTests.vb => SimplificationTests.vb} | 0 .../TestUtilities/Async/Checkpoint.cs | 2 +- .../TestUtilities/BlindAggregatorFactory.cs | 8 +- .../FormattedClassifications.Operators.cs | 2 +- ...ttedClassifications.OverloadedOperators.cs | 2 +- .../FormattedClassifications.Punctuation.cs | 2 +- .../FormattedClassifications.cs | 2 +- .../IDiagnosticServiceExtensions.cs | 20 - .../NavigateTo/AbstractNavigateToTests.cs | 38 +- .../NavigateToTestAggregator.Callback.cs | 3 +- .../Semantics/SpeculationAnalyzerTestsBase.cs | 2 +- .../TextEditorFactoryExtensions.cs | 2 +- .../AbstractTextStructureNavigationTests.cs | 2 +- .../Threading/WpfTestSharedData.cs | 4 +- .../Utilities/TestCommandExecutionContext.cs | 2 +- ...NoCompilationDocumentDiagnosticAnalyzer.cs | 2 +- .../Text/Extensions.TextBufferContainer.cs | 2 +- .../Extensions/ITextSnapshotExtensions.cs | 8 +- .../Shared/Extensions/TextSpanExtensions.cs | 4 +- .../DiagnosticAnalyzerDriverTests.vb | 2 +- .../Formatting/CodeCleanUpTests.vb | 1 - .../CSharpAddImportFeatureService.cs | 1 - ...eCodeRefactoringProvider.FixAllProvider.cs | 19 +- ...ecursivePatternsCodeRefactoringProvider.cs | 4 +- ...ConvertNamespaceCodeRefactoringProvider.cs | 6 +- ...nvertToExtensionCodeRefactoringProvider.cs | 4 +- .../ConvertToExtensionFixAllProvider.cs | 16 +- ...tringToRawStringCodeRefactoringProvider.cs | 4 +- .../EditAndContinue/SyntaxComparer.cs | 4 +- .../CSharpEncapsulateFieldService.cs | 9 +- .../Api/PythiaSignatureHelpItemWrapper.cs | 4 +- .../CSharpSyntaxTriviaService.cs | 2 +- ...harpDiagnosticAnalyzerQuickInfoProvider.cs | 31 +- .../AbstractCSharpSignatureHelpProvider.cs | 12 +- .../AttributeSignatureHelpProvider.cs | 2 +- ...seExpressionBodyCodeRefactoringProvider.cs | 4 +- ...oPropertyToFullPropertyTests_OptionSets.cs | 18 +- .../ConvertForEachToForTests.cs | 2 +- .../ConvertForToForEachTests.cs | 4 +- .../AllAnalyzersSeverityConfigurationTests.cs | 2 +- ...CategoryBasedSeverityConfigurationTests.cs | 2 +- ...SeverityBasedSeverityConfigurationTests.cs | 2 +- .../Suppression/RemoveSuppressionTests.cs | 2 +- ...emoveUnnecessaryPragmaSuppressionsTests.cs | 6 +- .../Suppression/SuppressionAllCodeTests.cs | 2 +- .../SuppressionTest_FixMultipleTests.cs | 4 +- .../Suppression/SuppressionTests.cs | 12 +- .../EditAndContinue/StatementMatchingTests.cs | 2 +- .../IntroduceVariableTests.cs | 2 +- .../UseExpressionBodyFixAllTests.cs | 2 +- ...ressionBodyForAccessorsRefactoringTests.cs | 16 +- ...essionBodyForPropertiesRefactoringTests.cs | 14 +- ...actAddFileBannerCodeRefactoringProvider.cs | 6 +- .../AbstractAddImportFeatureService.cs | 14 +- .../AddImport/SymbolReferenceFinder.cs | 20 +- .../Core/Portable/AddImport/SymbolResult.cs | 6 +- .../Portable/CodeFixes/CodeFixCollection.cs | 15 +- ...ConfigureCodeStyleOptionCodeFixProvider.cs | 2 +- .../ConfigureSeverityLevelCodeFixProvider.cs | 4 +- .../AbstractFixAllCodeFixCodeAction.cs | 58 - .../FixMultipleCodeAction.cs | 17 - ...CodeFixService.FixAllDiagnosticProvider.cs | 13 +- .../CodeFixes/Service/CodeFixService.cs | 139 +- .../CodeFixes/Service/ICodeFixService.cs | 10 +- .../AbstractSuppressionCodeFixProvider.cs | 6 +- .../AbstractFixAllCodeAction.cs | 89 +- .../AbstractFixAllGetFixesService.cs | 10 +- .../CodeActionRequestPriorityProvider.cs | 159 - .../IFixAllGetFixesService.cs | 4 +- .../CodeLens/CodeLensFindReferenceProgress.cs | 2 +- .../CodeRefactoringService.cs | 2 +- .../FixAllCodeRefactoringCodeAction.cs | 21 - .../AbstractDocCommentCompletionProvider.cs | 2 +- ...tensionMethodImportCompletionCacheEntry.cs | 2 +- ...onvertIfToSwitchCodeRefactoringProvider.cs | 4 +- ...ToInterpolatedStringRefactoringProvider.cs | 4 +- .../Copilot/ICopilotChangeAnalysisService.cs | 6 +- .../CodeAnalysisDiagnosticAnalyzerService.cs | 99 +- .../DiagnosticAnalyzerExtensions.cs | 3 - .../Diagnostics/IDiagnosticAnalyzerService.cs | 98 +- .../Options/DiagnosticOptionsStorage.cs | 3 - ...gnosticAnalyzerService.HostAnalyzerInfo.cs | 10 +- ...DiagnosticAnalyzerService.ProjectStates.cs | 2 +- .../Service/DiagnosticAnalyzerService.cs | 98 +- ...zerService_CompilationWithAnalyzersPair.cs | 2 +- ...lyzerService_DeprioritizationCandidates.cs | 70 +- ...zerService_ForceCodeAnalysisDiagnostics.cs | 102 + ...icAnalyzerService_GetDiagnosticsForSpan.cs | 148 +- ...alyzerService_ProduceProjectDiagnostics.cs | 102 +- ...AnalyzerService_RemoteOrLocalDispatcher.cs | 168 +- .../RudeEditDiagnosticsBuilder.cs | 4 +- .../DateAndTimeLanguageDetector.cs | 2 +- .../EmbeddedLanguageDetector.cs | 9 +- ...ordinator.UnitTestingAsyncWorkItemQueue.cs | 2 +- .../ExtractMethod/MethodExtractor.Analyzer.cs | 2 +- .../AbstractGenerateTypeService.Editor.cs | 2 +- ...AbstractInlineParameterNameHintsService.cs | 11 +- .../AbstractInlineTypeHintsService.cs | 2 +- .../Portable/InlineHints/InlineHintHelpers.cs | 9 +- .../Portable/Intents/IntentDataProvider.cs | 2 +- ...dataAsSourceService.WrappedMethodSymbol.cs | 2 + .../MoveStaticMembersWithDialogCodeAction.cs | 76 + .../NavigateTo/INavigateToSearcherHost.cs | 2 +- .../OrganizeImportsCodeRefactoringProvider.cs | 4 +- .../ImplementationAssemblyLookupService.cs | 2 +- .../RQName/Nodes/RQConstructedType.cs | 2 +- .../RQName/Nodes/RQMethodOrProperty.cs | 2 +- .../RQName/Nodes/RQUnconstructedType.cs | 2 +- .../AbstractSignatureHelpProvider.cs | 2 +- .../Structure/BlockStructureContext.cs | 2 +- .../SymbolSearchUpdateEngine.Update.cs | 4 +- ...CodeActionOrUserDiagnosticTest_NoEditor.cs | 2 +- ...erDiagnosticTest_NoEditor_OptionHelpers.cs | 12 +- .../AbstractCodeActionTest_NoEditor.cs | 16 +- .../AbstractSuppressionAllCodeTests.cs | 2 +- .../AbstractUserDiagnosticTest_NoEditor.cs | 2 +- .../Diagnostics/ParenthesesOptionsProvider.cs | 6 +- .../NamingStylesTestOptionSets.cs | 44 +- .../AspNetCoreVirtualCharSequence.cs | 2 +- .../EditAndContinueWorkspaceTestBase.cs | 2 +- .../Features/TestUtilities/Utils/Options.cs | 4 +- .../VisualBasicAddImportFeatureService.vb | 11 +- .../VisualDiagnosticsServiceFactory.cs | 4 +- .../LspFileChangeWatcherTests.cs | 2 +- .../BrokeredServices/ServiceBrokerFactory.cs | 2 +- .../BrokeredServiceBridgeManifestService.cs | 2 +- .../FileWatching/LspFileChangeWatcher.cs | 4 +- .../StarredCompletionsAssemblyHelper.cs | 2 +- .../TestExampleLanguageServer.cs | 4 +- .../RequestExecutionQueue.cs | 2 +- .../TestDiagnosticAnalyzerDriver.cs | 4 +- .../AbstractLanguageServerProtocolTests.cs | 82 +- .../Protocol/Extensions/Extensions.cs | 2 +- .../Extensions/ProtocolConversions.cs | 8 +- .../CodeCleanup/AbstractCodeCleanupService.cs | 9 +- ...nguageServerProjectSystemOptionsStorage.cs | 6 +- .../Features/Suggestions/SuggestedAction.cs | 123 + .../Suggestions/SuggestedActionFlavors.cs | 11 + .../Suggestions/SuggestedActionSet.cs | 27 + ...dPredefinedSuggestedActionCategoryNames.cs | 2 +- .../UnifiedSuggestedActionSetComparer.cs | 6 +- .../UnifiedSuggestedActionsSource.cs | 329 +- .../UnifiedSuggestedActionSet.cs | 44 - .../ICodeFixSuggestedAction.cs | 16 - .../ICodeRefactoringSuggestedAction.cs | 16 - .../IFixAllCodeFixSuggestedAction.cs | 21 - .../IFixAllCodeRefactoringSuggestedAction.cs | 19 - .../IUnifiedSuggestedAction.cs | 19 - .../UnifiedCodeFixSuggestedAction.cs | 36 - .../UnifiedCodeRefactoringSuggestedAction.cs | 32 - .../UnifiedFixAllCodeFixSuggestedAction.cs | 32 - ...iedFixAllCodeRefactoringSuggestedAction.cs | 28 - .../UnifiedSuggestedAction.cs | 20 - ...UnifiedSuggestedActionWithNestedActions.cs | 31 - .../Protocol/Handler/BufferedProgress.cs | 2 +- .../CodeActionFixAllResolveHandler.cs | 3 +- .../Handler/CodeActions/CodeActionHelpers.cs | 104 +- .../Handler/CodeActions/FixAllCodeAction.cs | 29 - .../BuildOnlyDiagnosticIdsHandler.cs | 44 +- ...mentsAndProjectDiagnosticSourceProvider.cs | 12 +- .../AbstractProjectDiagnosticSource.cs | 9 +- ...stractWorkspaceDocumentDiagnosticSource.cs | 10 +- .../NonLocalDocumentDiagnosticSource.cs | 5 +- ...ocumentNonLocalDiagnosticSourceProvider.cs | 4 +- .../DocumentChanges/DidChangeHandler.cs | 4 +- .../Handler/DocumentChanges/DidOpenHandler.cs | 2 +- .../Formatting/FormatDocumentOnTypeHandler.cs | 58 +- .../Handler/IDocumentChangeTracker.cs | 8 +- .../InlineCompletions/XmlSnippetParser.cs | 2 +- .../OnAutoInsert/OnAutoInsertHandler.cs | 15 +- .../GetTextDocumentWithContextHandler.cs | 2 +- .../Protocol/Handler/RequestContext.cs | 16 +- .../Protocol/LanguageInfoProvider.cs | 2 +- .../LanguageServer/Protocol/NoOpLspLogger.cs | 2 +- .../Protocol/Converters/SumConverter.cs | 2 +- .../Protocol/Protocol/Extensions/VSMethods.cs | 2 +- .../OptimizedVSCompletionListJsonConverter.cs | 2 +- .../Protocol/Internal/VSInternalMethods.cs | 20 +- .../Protocol/Protocol/SumType.cs | 18 +- .../Workspaces/LspWorkspaceManager.cs | 28 +- .../Workspaces/TrackedDocumentInfo.cs | 16 + .../CodeActions/CodeActionResolveTests.cs | 6 +- .../CodeActions/CodeActionsTests.cs | 2 +- .../AbstractPullDiagnosticTestsBase.cs | 6 +- .../AdditionalFileDiagnosticsTests.cs | 2 +- .../DocumentChanges/DocumentChangesTests.cs | 75 +- .../FoldingRanges/FoldingRangesTests.cs | 2 +- .../Formatting/FormatDocumentOnTypeTests.cs | 67 +- .../Formatting/FormatDocumentRangeTests.cs | 2 +- .../Formatting/FormatDocumentTests.cs | 2 +- .../Highlights/DocumentHighlightTests.cs | 2 +- .../ProtocolUnitTests/MapCode/MapCodeTests.cs | 2 +- .../OnAutoInsert/OnAutoInsertTests.cs | 140 +- .../GetTextDocumentWithContextHandlerTests.cs | 2 +- .../FindAllReferencesHandlerTests.cs | 2 +- .../Rename/PrepareRenameTests.cs | 2 +- .../ProtocolUnitTests/Rename/RenameTests.cs | 2 +- .../AbstractSemanticTokensTests.cs | 6 +- .../SignatureHelp/SignatureHelpTests.cs | 4 +- .../SimplifyMethod/SimplifyMethodTests.cs | 2 +- .../Microsoft.Net.Compilers.Toolset.props | 3 + ...Net.Compilers.Toolset.Framework.Core.props | 1 + ...icrosoft.Net.Compilers.Toolset.Arm64.props | 1 + ...rosoft.CodeAnalysis.Metrics.Package.csproj | 2 + .../Metrics.Legacy/Metrics.Legacy.csproj | 2 + .../Tools/Metrics/Metrics.csproj | 2 + .../DisposeAnalysis/DisposeAnalysisHelper.cs | 3 +- .../Framework/DataFlow/DataFlowAnalysis.cs | 3 +- .../DataFlow/LValueFlowCapturesProvider.cs | 3 +- .../BuildBoss/CompilerNuGetCheckerUtil.cs | 5 +- .../AbstractRazorCohostLifecycleService.cs | 17 - .../Razor/Features/Cohost/Constants.cs | 2 - .../Features/Cohost/Handlers/OnAutoInsert.cs | 2 +- .../Features/Cohost/ICohostStartupService.cs | 1 - .../Cohost/RazorStartupServiceFactory.cs | 60 +- .../src/Tools/PrepareTests/MinimizeUtil.cs | 51 +- src/roslyn/src/Tools/Replay/Replay.cs | 91 +- src/roslyn/src/Tools/Replay/Replay.csproj | 2 +- .../Apis/Microsoft.CodeAnalysis.txt | 4 + .../Impl/Options/Formatting/StyleViewModel.cs | 36 +- .../CSharpSnippetExpansionLanguageHelper.cs | 2 +- .../AbstractPersistentStorageTests.cs | 38 +- .../ChangeSignatureDialogViewModel.cs | 4 +- .../IVisualStudioDiagnosticAnalyzerService.cs | 2 +- .../VisualStudioDiagnosticAnalyzerService.cs | 18 +- .../DocumentSymbolDataViewModel.cs | 4 +- ...StudioDiagnosticAnalyzerServiceAccessor.cs | 8 +- ...tudioGlobalOperationNotificationService.cs | 4 +- ...per.LSPContainedDocumentServiceProvider.cs | 4 +- ...ualStudioExtractInterfaceOptionsService.cs | 2 +- .../GenerateTypeDialogViewModel.cs | 2 +- ...geService`2.IVsContainedLanguageFactory.cs | 2 +- ...vice`2.IVsImmediateStatementCompletion2.cs | 10 +- ...tractLanguageService`2.IVsLanguageBlock.cs | 5 +- ...ageService`2.IVsLanguageContextProvider.cs | 4 +- ...tLanguageService`2.IVsLanguageDebugInfo.cs | 14 +- ...actLanguageService`2.IVsLanguageTextOps.cs | 9 +- ...ctLanguageService`2.VsCodeWindowManager.cs | 8 +- ...ctLanguageService`2.VsLanguageDebugInfo.cs | 16 +- .../AbstractLanguageService`2.cs | 90 +- .../Def/LanguageService/AbstractPackage`2.cs | 36 +- .../Def/Options/VisualStudioOptionStorage.cs | 1 - .../PdbSourceDocumentOutputWindowLogger.cs | 2 +- .../VisualStudioPickMembersService.cs | 4 +- .../Legacy/AbstractLegacyProject.cs | 4 - .../Legacy/SolutionEventsBatchScopeCreator.cs | 273 +- .../Def/Snippets/SnippetExpansionClient.cs | 2 +- .../VisualStudioSuppressionFixService.cs | 4 +- .../Core/Def/Telemetry/CodeMarkerLogger.cs | 6 +- .../Core/Def/Utilities/ClipboardHelpers.cs | 2 +- .../CodeModel/AbstractCodeModelService.cs | 3 +- .../Impl/CodeModel/CodeModelExtensions.cs | 2 +- .../Impl/CodeModel/CodeModelProjectCache.cs | 4 +- .../CodeModel/InternalElements/CodeClass.cs | 2 +- .../AbstractMethodXmlBuilder.AttributeInfo.cs | 2 +- .../MethodXml/AbstractMethodXmlBuilder.cs | 4 +- .../Core/Impl/CodeModel/ProjectCodeModel.cs | 2 +- .../Options/AbstractOptionPreviewViewModel.cs | 4 +- .../Options/CodeStyleNoticeTextBlock.xaml.cs | 2 +- .../Options/GridOptionPreviewControl.xaml.cs | 2 +- .../NamingStyleOptionPageViewModel.cs | 7 +- .../SymbolSpecificationViewModel.cs | 6 +- .../Venus/DocumentService_IntegrationTests.vb | 2 +- .../FSharpBreakpointResolutionResult.cs | 4 +- .../FSharpSimplifyNameDiagnosticAnalyzer.cs | 2 +- .../FSharpUnusedDeclarationsAnalyzer.cs | 2 +- .../FSharpUnusedOpensDiagnosticAnalyzer.cs | 2 +- .../InProcess/EditorInProcess.cs | 24 +- .../InProcess/SolutionExplorerInProcess.cs | 4 +- ...nguageServices.New.IntegrationTests.csproj | 1 + .../Client/RemoteLanguageServiceWorkspace.cs | 2 +- .../RemoteLanguageServiceWorkspaceHost.cs | 2 +- .../CustomProtocol/LspRequestExtensions.cs | 2 +- .../Impl/LiveShareInitializeHandler.cs | 2 +- .../LiveShare/Test/ProjectsHandlerTests.cs | 2 +- .../TestUtilities2/MockServiceProvider.vb | 21 +- .../Framework/TestEnvironment.vb | 44 +- .../Guids.cs | 2 +- .../Loggers/OutputWindowLogger.cs | 4 +- .../OptionPages/ForceLowMemoryMode.cs | 2 +- .../PerfMargin/StatusIndicator.xaml.cs | 4 +- .../VenusMargin/ProjectionSpanTag.cs | 2 +- .../VenusMargin/VenusMargin.cs | 2 +- .../Handler/Completion/CompletionHandler.cs | 2 +- .../DocumentPullDiagnosticHandler.cs | 2 +- .../WorkspacePullDiagnosticHandler.cs | 2 +- .../Impl/Implementation/XamlProjectService.cs | 2 +- .../XamlTextViewCreationListener.cs | 2 +- .../CodeGeneration/CSharpSyntaxGenerator.cs | 14 +- .../CSharpCommandLineParserServiceTests.cs | 2 +- .../CodeGeneration/SyntaxGeneratorTests.cs | 352 ++ .../Workspace/Host/Mef/MefV1HostServices.cs | 2 +- .../Core/Portable/CodeFixes/CodeFix.cs | 57 +- .../Portable/CodeFixes/CodeFixProvider.cs | 3 +- .../DocumentBasedFixAllProvider.cs | 1 - .../FixAllOccurrences/FixAllContext.cs | 10 +- .../FixAllOccurrences/FixAllProvider.cs | 6 +- .../FixAllOccurrences/FixAllScope.cs | 8 +- .../CommonFixAllState.cs | 14 +- .../DefaultFixAllProviderHelpers.cs | 8 +- .../DocumentBasedFixAllProviderHelpers.cs | 2 +- .../CodeFixesAndRefactorings/FixAllKind.cs | 2 +- .../CodeFixesAndRefactorings/FixAllLogger.cs | 2 +- .../FixAllProviderInfo.cs | 33 +- ...Context.cs => IRefactorOrFixAllContext.cs} | 13 +- ...ovider.cs => IRefactorOrFixAllProvider.cs} | 16 +- ...xAllState.cs => IRefactorOrFixAllState.cs} | 13 +- .../IRefactorOrFixProvider.cs | 9 + .../CodeRefactoringProvider.cs | 11 +- .../DocumentBasedFixAllProvider.cs | 102 - .../DocumentBasedRefactorAllProvider.cs | 104 + .../FixAllOccurences/FixAllProvider.cs | 106 - ...FixAllContext.cs => RefactorAllContext.cs} | 75 +- .../FixAllOccurences/RefactorAllProvider.cs | 107 + .../FixAllOccurences/RefactorAllScope.cs | 92 + .../{FixAllState.cs => RefactorAllState.cs} | 71 +- ...yntaxEditorBasedCodeRefactoringProvider.cs | 47 +- .../Portable/Diagnostics/AnalyzerFilter.cs | 30 + .../CodeAnalysisEventSource.Workspaces.cs | 2 +- .../DiagnosticAnalyzerInfoCache.cs | 25 +- .../Portable/Diagnostics/DiagnosticData.cs | 22 +- .../Diagnostics/DiagnosticIdFilter.cs | 65 + .../Portable/Diagnostics/DiagnosticKind.cs | 0 .../Core/Portable/Diagnostics/Extensions.cs | 20 - .../Diagnostics/HostDiagnosticAnalyzers.cs | 21 +- .../IRemoteDiagnosticAnalyzerService.cs | 48 +- .../Core/Portable/Editing/SyntaxGenerator.cs | 67 +- .../Pythia/Api/PythiaEditDistanceWrapper.cs | 2 +- .../PatternMatcher.PatternSegment.cs | 2 +- .../Core/Portable/PublicAPI.Unshipped.txt | 26 +- .../ConflictingIdentifierTracker.cs | 2 +- .../Core/Portable/Rename/RenameUtilities.cs | 2 +- .../Extensions/ILanguageMetadataExtensions.cs | 4 +- .../DocumentationProviderServiceFactory.cs | 3 +- .../Host/Metadata/MetadataServiceFactory.cs | 2 +- ...pilationState.RegularCompilationTracker.cs | 2 +- .../Workspace/Solution/SolutionState.cs | 2 +- .../Portable/Workspace/WorkspaceEventMap.cs | 4 +- .../CoreTest/BatchFixAllProviderTests.cs | 2 +- .../LongestCommonSubsequenceTests.cs | 2 +- .../CoreTest/Differencing/TestTreeComparer.cs | 4 +- .../CoreTest/Editing/SyntaxEditorTests.cs | 2 +- .../CoreTest/ExtensionOrdererTests.cs | 2 +- .../MEF/ExportProviderCache.cs | 4 +- .../CoreTestUtilities/MEF/TestComposition.cs | 2 +- .../Options/OptionsTestHelpers.cs | 2 +- .../Workspaces/TestHostProject`1.cs | 2 +- .../Workspaces/TestWorkspace_XmlCreation.cs | 2 +- .../Workspaces/MSBuild/BuildHost/BuildHost.cs | 34 +- .../Workspaces/MSBuild/BuildHost/Program.cs | 19 +- .../BuildHost/Rpc/Contracts/IBuildHost.cs | 7 + .../BuildHost/Rpc/Contracts/JsonSettings.cs | 2 +- .../MSBuild/BuildHost/Rpc/RpcServer.cs | 4 +- .../Core/MSBuild/BuildHostProcessManager.cs | 30 +- .../MSBuild/Core/Rpc/RemoteBuildHost.cs | 7 +- .../Workspaces/MSBuild/Core/Rpc/RpcClient.cs | 4 +- .../Test/BuildHostProcessManagerTests.cs | 79 +- .../Test/NewlyCreatedProjectsFromDotNetNew.cs | 10 +- .../Remote/Core/ExportProviderBuilder.cs | 6 +- .../Serialization/MessagePackFormatters.cs | 2 +- .../ServiceHub/Host/RemoteWorkspaceManager.cs | 2 +- .../DiagnosticAnalyzer/DiagnosticComputer.cs | 493 -- .../DiagnosticAnalyzer/PerformanceQueue.cs | 2 +- .../PerformanceTrackerService.cs | 2 +- .../RemoteDiagnosticAnalyzerService.cs | 151 +- .../EditAndContinueLogReporter.cs | 4 +- .../Services/SyntaxFacts/CSharpSyntaxKinds.cs | 1 + .../Core/CompilerExtensions.projitems | 1 - ....DataFlowAnalyzer.FlowGraphAnalysisData.cs | 2 +- .../SymbolUsageAnalysis/SymbolUsageResult.cs | 4 +- .../SyntaxFacts/ISyntaxFactsExtensions.cs | 159 +- .../Core/Services/SyntaxFacts/ISyntaxKinds.cs | 1 + .../Compiler/Core/Utilities/EditDistance.cs | 2 +- .../Compilation/CompilationExtensions.cs | 3 + .../Extensions/Symbols/ISymbolExtensions.cs | 32 +- .../SyntaxFacts/VisualBasicSyntaxKinds.vb | 1 + .../SyntaxEditorBasedCodeFixProvider.cs | 16 +- .../IFixAllSpanMappingService.cs | 2 +- .../Core/CodeGeneration/CodeGenerator.cs | 25 +- .../CodeGenerationAbstractMethodSymbol.cs | 2 + .../CodeGenerationAbstractNamedTypeSymbol.cs | 6 + .../Symbols/CodeGenerationTypeSymbol.cs | 4 +- .../Core/Extensions/ISolutionExtensions.cs | 20 + .../Core/Extensions/ProjectExtensions.cs | 23 +- .../VisualBasicSyntaxGenerator.vb | 5 + .../CodeGeneration/SyntaxGeneratorTests.vb | 17 + src/source-manifest.json | 4 +- 606 files changed, 17015 insertions(+), 8087 deletions(-) create mode 100644 src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/RefInitializationHoister.cs delete mode 100644 src/roslyn/src/EditorFeatures/Core/Suggestions/Copilot/FlavoredSuggestedAction.cs delete mode 100644 src/roslyn/src/EditorFeatures/Core/Suggestions/PreviewChanges/PreviewChangesSuggestedAction.cs delete mode 100644 src/roslyn/src/EditorFeatures/Core/Suggestions/RefineUsingCopilot/RefineUsingCopilotSuggestedAction.cs delete mode 100644 src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionPriorityProvider.cs delete mode 100644 src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/CodeFixSuggestedAction.cs delete mode 100644 src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/CodeRefactoringSuggestedAction.cs rename src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/{SuggestedAction.CaretPositionRestorer.cs => EditorSuggestedAction.CaretPositionRestorer.cs} (98%) rename src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/{SuggestedAction.cs => EditorSuggestedAction.cs} (82%) rename src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/{AbstractFixAllSuggestedAction.cs => EditorSuggestedActionForRefactorOrFixAll.cs} (62%) rename src/roslyn/src/EditorFeatures/Core/Suggestions/{SuggestedActionWithNestedActions.cs => SuggestedActions/EditorSuggestedActionWithNestedActions.cs} (50%) rename src/roslyn/src/EditorFeatures/Core/Suggestions/{SuggestedActionWithNestedFlavors.cs => SuggestedActions/EditorSuggestedActionWithNestedFlavors.cs} (60%) delete mode 100644 src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.FixAllCodeAction.cs delete mode 100644 src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.cs delete mode 100644 src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs rename src/roslyn/src/EditorFeatures/Test2/Diagnostics/{AbstractCrossLanguageUserDiagnosticTest.vb => AbstractCrossLanguageUserDiagnosticTests.vb} (98%) rename src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddImport/{AddImportCrossLanguageTests.vb => AddImportCrossLanguageUserDiagnosticTests.vb} (99%) rename src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddMissingReference/{AddMissingReferenceTests.vb => AddMissingReferenceCrossLanguageUserDiagnosticTests.vb} (98%) rename src/roslyn/src/EditorFeatures/Test2/Diagnostics/{AdditionalFileDiagnosticsTests.vb => AdditionalFileCrossLanguageUserDiagnosticTests.vb} (97%) rename src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateEvent/{GenerateEventCrossLanguageTests.vb => GenerateEventCrossLanguageUserDiagnosticTests.vb} (97%) rename src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/{GenerateConstructorCrossLanguageTests.vb => GenerateConstructorCrossLanguageUserDiagnosticTests.vb} (94%) rename src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/{GenerateMethodCrossLanguageTests.vb => GenerateMethodCrossLanguageUserDiagnosticTests.vb} (99%) rename src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/{GenerateVariableCrossLanguageTests.vb => GenerateVariableCrossLanguageUserDiagnosticTests.vb} (95%) rename src/roslyn/src/EditorFeatures/Test2/Diagnostics/ImplementInterface/{ImplementInterfaceCrossLanguageTests.vb => ImplementInterfaceCrossLanguageUserDiagnosticTests.vb} (98%) rename src/roslyn/src/EditorFeatures/Test2/Diagnostics/UseAutoProperty/{UseAutoPropertyTests.vb => UseAutoPropertyCrossLanguageUserDiagnosticTests.vb} (98%) rename src/roslyn/src/EditorFeatures/Test2/GoToBase/{VisuaBasicGoToBaseTests.vb => VisualBasicGoToBaseTests.vb} (100%) rename src/roslyn/src/EditorFeatures/Test2/IntelliSense/{CSharpCompletionCommandHandlerTests_AwaitCompletion.vb => CSharpCompletionCommandHandlerTests_Await.vb} (100%) rename src/roslyn/src/EditorFeatures/Test2/IntelliSense/{CompletionServiceTests_Exclusivitiy.vb => CompletionServiceTests_Exclusivity.vb} (100%) rename src/roslyn/src/EditorFeatures/Test2/IntelliSense/{VisualBasicCompletionCommandHandlerTests_AwaitCompletion.vb => VisualBasicCompletionCommandHandlerTests_Await.vb} (100%) rename src/roslyn/src/EditorFeatures/Test2/Simplification/{ModuleNameSimplificationTests.vb => ModuleNameSimplifierTest.vb} (100%) rename src/roslyn/src/EditorFeatures/Test2/Simplification/{BlockSimplificationTests.vb => SimplificationTests.vb} (100%) delete mode 100644 src/roslyn/src/EditorFeatures/TestUtilities/Extensions/IDiagnosticServiceExtensions.cs delete mode 100644 src/roslyn/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/AbstractFixAllCodeFixCodeAction.cs delete mode 100644 src/roslyn/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/FixMultipleCodeAction.cs delete mode 100644 src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/CodeActionRequestPriorityProvider.cs delete mode 100644 src/roslyn/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs create mode 100644 src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_ForceCodeAnalysisDiagnostics.cs create mode 100644 src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedAction.cs create mode 100644 src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedActionFlavors.cs create mode 100644 src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedActionSet.cs rename src/roslyn/src/LanguageServer/Protocol/Features/{UnifiedSuggestions => Suggestions}/UnifiedPredefinedSuggestedActionCategoryNames.cs (92%) rename src/roslyn/src/LanguageServer/Protocol/Features/{UnifiedSuggestions => Suggestions}/UnifiedSuggestedActionSetComparer.cs (92%) rename src/roslyn/src/LanguageServer/Protocol/Features/{UnifiedSuggestions => Suggestions}/UnifiedSuggestedActionsSource.cs (65%) delete mode 100644 src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionSet.cs delete mode 100644 src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/ICodeFixSuggestedAction.cs delete mode 100644 src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/ICodeRefactoringSuggestedAction.cs delete mode 100644 src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IFixAllCodeFixSuggestedAction.cs delete mode 100644 src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IFixAllCodeRefactoringSuggestedAction.cs delete mode 100644 src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IUnifiedSuggestedAction.cs delete mode 100644 src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedCodeFixSuggestedAction.cs delete mode 100644 src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedCodeRefactoringSuggestedAction.cs delete mode 100644 src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeFixSuggestedAction.cs delete mode 100644 src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs delete mode 100644 src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedAction.cs delete mode 100644 src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedActionWithNestedActions.cs delete mode 100644 src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/FixAllCodeAction.cs create mode 100644 src/roslyn/src/LanguageServer/Protocol/Workspaces/TrackedDocumentInfo.cs delete mode 100644 src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/AbstractRazorCohostLifecycleService.cs rename src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/{IFixAllContext.cs => IRefactorOrFixAllContext.cs} (69%) rename src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/{IFixAllProvider.cs => IRefactorOrFixAllProvider.cs} (55%) rename src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/{IFixAllState.cs => IRefactorOrFixAllState.cs} (71%) create mode 100644 src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixProvider.cs delete mode 100644 src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs create mode 100644 src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedRefactorAllProvider.cs delete mode 100644 src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs rename src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/{FixAllContext.cs => RefactorAllContext.cs} (50%) create mode 100644 src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllProvider.cs create mode 100644 src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllScope.cs rename src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/{FixAllState.cs => RefactorAllState.cs} (51%) create mode 100644 src/roslyn/src/Workspaces/Core/Portable/Diagnostics/AnalyzerFilter.cs create mode 100644 src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticIdFilter.cs rename src/roslyn/src/{Features => Workspaces}/Core/Portable/Diagnostics/DiagnosticKind.cs (100%) delete mode 100644 src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs diff --git a/src/roslyn/azure-pipelines-official.yml b/src/roslyn/azure-pipelines-official.yml index b3869f8e8ae..e8c7a44edcc 100644 --- a/src/roslyn/azure-pipelines-official.yml +++ b/src/roslyn/azure-pipelines-official.yml @@ -112,6 +112,8 @@ extends: parameters: featureFlags: autoBaseline: true + autoEnablePREfastWithNewRuleset: false + autoEnableRoslynWithNewRuleset: false sdl: sourceAnalysisPool: name: NetCore1ESPool-Svc-Internal @@ -410,9 +412,6 @@ extends: - template: /eng/common/templates-official/post-build/post-build.yml@self parameters: publishingInfraVersion: 3 - # Symbol validation is not entirely reliable as of yet, so should be turned off until - # https://github.com/dotnet/arcade/issues/2871 is resolved. - enableSymbolValidation: false enableSourceLinkValidation: false # Enable SDL validation, passing through values from the 'DotNet-Roslyn-SDLValidation-Params' group. SDLValidationParameters: diff --git a/src/roslyn/azure-pipelines.yml b/src/roslyn/azure-pipelines.yml index d2d30e15a4a..079ad3505d3 100644 --- a/src/roslyn/azure-pipelines.yml +++ b/src/roslyn/azure-pipelines.yml @@ -125,6 +125,10 @@ parameters: default: name: $(PoolName) demands: ImageOverride -equals $(WindowsQueueName) + - name: macOSPool + type: object + default: + vmImage: macOS-15 - name: vs2022PreviewPool type: object default: @@ -170,6 +174,16 @@ stages: testArtifactName: Transport_Artifacts_Unix_Debug poolParameters: ${{ parameters.ubuntuPool }} +- ${{ if ne(variables['Build.Reason'], 'PullRequest') }}: + - stage: MacOS_Build + dependsOn: [] + jobs: + - template: eng/pipelines/build-unix-job.yml + parameters: + jobName: Build_macOS_Debug + testArtifactName: Transport_Artifacts_macOS_Debug + poolParameters: ${{ parameters.macOSPool }} + - stage: Source_Build dependsOn: [] jobs: @@ -370,14 +384,19 @@ stages: testArguments: --testCoreClr poolParameters: ${{ parameters.ubuntuPool }} - # https://github.com/dotnet/runtime/issues/97186 - # Disabled until runtime can track down the crash - - ${{ if ne(variables['Build.Reason'], 'PullRequest') }}: +- ${{ if ne(variables['Build.Reason'], 'PullRequest') }}: + - stage: MacOS_Debug_CoreClr + dependsOn: MacOS_Build + variables: + - ${{ if ne(variables['System.TeamProject'], 'public') }}: + - group: DotNet-HelixApi-Access + jobs: + # https://github.com/dotnet/runtime/issues/97186 - template: eng/pipelines/test-unix-job.yml parameters: testRunName: 'Test macOS Debug' jobName: Test_macOS_Debug - testArtifactName: Transport_Artifacts_Unix_Debug + testArtifactName: Transport_Artifacts_macOS_Debug configuration: Debug testArguments: --testCoreClr helixQueueName: $(HelixMacOsQueueName) diff --git a/src/roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md b/src/roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md index 816c2435e2a..a79569ad0a3 100644 --- a/src/roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md +++ b/src/roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md @@ -7,7 +7,7 @@ This document lists known breaking changes in Roslyn after .NET 9 general releas ***Introduced in Visual Studio 2022 version 17.13*** C# 14 introduces the ability to write a lambda with parameter modifiers, without having to specify a parameter type: -https://github.com/dotnet/csharplang/blob/main/proposals/simple-lambda-parameters-with-modifiers.md +[Simple lambda parameters with modifiers](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-14.0/simple-lambda-parameters-with-modifiers.md) As part of this work, a breaking change was accepted where `scoped` will always be treated as a modifier in a lambda parameter, even where it might have been accepted as a type name in the past. For example: diff --git a/src/roslyn/docs/contributing/Compiler Test Plan.md b/src/roslyn/docs/contributing/Compiler Test Plan.md index bc9d292891d..f17dddeb8ba 100644 --- a/src/roslyn/docs/contributing/Compiler Test Plan.md +++ b/src/roslyn/docs/contributing/Compiler Test Plan.md @@ -35,6 +35,14 @@ This document provides guidance for thinking about language interactions and tes - Can build VS - Check that `Obsolete` is honored for members used in binding/lowering - LangVersion + +- Does the feature use cryptographic hashes in any way? (examples: metadata names of file-local types, extension types, assembly strong naming, PDB document table, etc.) + - Consider using non-cryptographic hash such as `XxHash128` instead. + - If you must use a cryptographic hash in the feature implementation, then use `SourceHashAlgorithms.Default`, and not any specific hash. + - A cryptographic hash must never be included in a public API name. Taking a change to the default crypto algorithm would then change public API surface, which would be enormously breaking. + - **DO NOT** allow using the value of a crypto hash in a field, method or type name + - **DO** allow using the value of a crypto hash in attribute or field values + - Any time the compiler reads in metadata containing crypto hashes, even if it's an attribute value, ensure the crypto hash algorithm name is included in the metadata (e.g. prefixing it to the hash value), so that it can be changed over time and the compiler can continue to read both metadata using both the old and new algorithms. # Type and members - Access modifiers (public, protected, internal, protected internal, private protected, private), static, ref diff --git a/src/roslyn/eng/Directory.Packages.props b/src/roslyn/eng/Directory.Packages.props index 1e8a1d46394..37ee560eb8a 100644 --- a/src/roslyn/eng/Directory.Packages.props +++ b/src/roslyn/eng/Directory.Packages.props @@ -38,10 +38,10 @@ - - - - + + + + @@ -75,6 +75,7 @@ + @@ -117,6 +118,7 @@ + - 2.0.0-rc.2.25427.104 + 2.0.0-rc.2.25462.117 9.0.0 9.0.0 @@ -38,9 +38,9 @@ This file should be imported by eng/Versions.props 9.0.0 9.0.0 - 10.0.0-beta.25427.2 - 10.0.0-beta.25427.2 - 10.0.0-beta.25427.2 + 11.0.0-beta.25462.2 + 11.0.0-beta.25462.2 + 11.0.0-beta.25462.2 2.0.0 diff --git a/src/roslyn/eng/Version.Details.xml b/src/roslyn/eng/Version.Details.xml index 6792bd1be72..b854fa39af2 100644 --- a/src/roslyn/eng/Version.Details.xml +++ b/src/roslyn/eng/Version.Details.xml @@ -1,15 +1,15 @@ - + https://github.com/dotnet/roslyn ae1fff344d46976624e68ae17164e0607ab68b10 - + https://github.com/dotnet/dotnet - 7ac1ca67bb1fb8a381c1c94a9f82a97725f0ccf3 + ee760c42a6df115208fe2262d358be6d4003e55f @@ -115,13 +115,13 @@ - + https://github.com/dotnet/arcade - ee3cae9ed3ef1990505e891831163ef34220d4e0 + c32cd132a730a7b9f947498b2ae75dbdc6785456 - + https://github.com/dotnet/arcade - ee3cae9ed3ef1990505e891831163ef34220d4e0 + c32cd132a730a7b9f947498b2ae75dbdc6785456 https://github.com/dotnet/symreader @@ -131,9 +131,9 @@ https://github.com/dotnet/roslyn 5d10d428050c0d6afef30a072c4ae68776621877 - + https://github.com/dotnet/arcade - ee3cae9ed3ef1990505e891831163ef34220d4e0 + c32cd132a730a7b9f947498b2ae75dbdc6785456 https://github.com/dotnet/roslyn-analyzers diff --git a/src/roslyn/eng/Versions.props b/src/roslyn/eng/Versions.props index aec4f310cca..25a52871980 100644 --- a/src/roslyn/eng/Versions.props +++ b/src/roslyn/eng/Versions.props @@ -100,6 +100,17 @@ + + + 17.10.29 + 8.0.5 + true false diff --git a/src/roslyn/eng/build.sh b/src/roslyn/eng/build.sh index caf46e4825c..a87e4b2a131 100755 --- a/src/roslyn/eng/build.sh +++ b/src/roslyn/eng/build.sh @@ -341,6 +341,7 @@ function GetCompilerTestAssembliesIncludePaths { assemblies+=" --include '^Microsoft\.CodeAnalysis\.VisualBasic\.Emit\.UnitTests$'" assemblies+=" --include '^Roslyn\.Compilers\.VisualBasic\.IOperation\.UnitTests$'" assemblies+=" --include '^Microsoft\.CodeAnalysis\.VisualBasic\.CommandLine\.UnitTests$'" + assemblies+=" --include '^Microsoft\.Build\.Tasks\.CodeAnalysis\.UnitTests$'" echo "$assemblies" } diff --git a/src/roslyn/eng/common/core-templates/job/publish-build-assets.yml b/src/roslyn/eng/common/core-templates/job/publish-build-assets.yml index 348cd16376f..37dff559fc1 100644 --- a/src/roslyn/eng/common/core-templates/job/publish-build-assets.yml +++ b/src/roslyn/eng/common/core-templates/job/publish-build-assets.yml @@ -40,6 +40,8 @@ parameters: repositoryAlias: self + officialBuildId: '' + jobs: - job: Asset_Registry_Publish @@ -62,6 +64,11 @@ jobs: value: false # unconditional - needed for logs publishing (redactor tool version) - template: /eng/common/core-templates/post-build/common-variables.yml + - name: OfficialBuildId + ${{ if ne(parameters.officialBuildId, '') }}: + value: ${{ parameters.officialBuildId }} + ${{ else }}: + value: $(Build.BuildNumber) pool: # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) @@ -124,7 +131,7 @@ jobs: /p:ManifestsPath='$(Build.StagingDirectory)/AssetManifests' /p:IsAssetlessBuild=${{ parameters.isAssetlessBuild }} /p:MaestroApiEndpoint=https://maestro.dot.net - /p:OfficialBuildId=$(Build.BuildNumber) + /p:OfficialBuildId=$(OfficialBuildId) condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} diff --git a/src/roslyn/eng/common/core-templates/jobs/jobs.yml b/src/roslyn/eng/common/core-templates/jobs/jobs.yml index b637cb6e948..01ada747665 100644 --- a/src/roslyn/eng/common/core-templates/jobs/jobs.yml +++ b/src/roslyn/eng/common/core-templates/jobs/jobs.yml @@ -44,6 +44,7 @@ parameters: artifacts: {} is1ESPipeline: '' repositoryAlias: self + officialBuildId: '' # Internal resources (telemetry, microbuild) can only be accessed from non-public projects, # and some (Microbuild) should only be applied to non-PR cases for internal builds. @@ -116,3 +117,4 @@ jobs: artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }} repositoryAlias: ${{ parameters.repositoryAlias }} + officialBuildId: ${{ parameters.officialBuildId }} diff --git a/src/roslyn/eng/common/core-templates/steps/generate-sbom.yml b/src/roslyn/eng/common/core-templates/steps/generate-sbom.yml index c05f6502797..003f7eae0fa 100644 --- a/src/roslyn/eng/common/core-templates/steps/generate-sbom.yml +++ b/src/roslyn/eng/common/core-templates/steps/generate-sbom.yml @@ -5,7 +5,7 @@ # IgnoreDirectories - Directories to ignore for SBOM generation. This will be passed through to the CG component detector. parameters: - PackageVersion: 10.0.0 + PackageVersion: 11.0.0 BuildDropPath: '$(System.DefaultWorkingDirectory)/artifacts' PackageName: '.NET' ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom diff --git a/src/roslyn/eng/common/core-templates/steps/source-index-stage1-publish.yml b/src/roslyn/eng/common/core-templates/steps/source-index-stage1-publish.yml index e9a694afa58..eff4573c6e5 100644 --- a/src/roslyn/eng/common/core-templates/steps/source-index-stage1-publish.yml +++ b/src/roslyn/eng/common/core-templates/steps/source-index-stage1-publish.yml @@ -1,6 +1,6 @@ parameters: - sourceIndexUploadPackageVersion: 2.0.0-20250818.1 - sourceIndexProcessBinlogPackageVersion: 1.0.1-20250818.1 + sourceIndexUploadPackageVersion: 2.0.0-20250906.1 + sourceIndexProcessBinlogPackageVersion: 1.0.1-20250906.1 sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json binlogPath: artifacts/log/Debug/Build.binlog diff --git a/src/roslyn/global.json b/src/roslyn/global.json index ed08b2111bd..228f656cf87 100644 --- a/src/roslyn/global.json +++ b/src/roslyn/global.json @@ -1,19 +1,19 @@ { "sdk": { - "version": "10.0.100-preview.7.25380.108", + "version": "10.0.100-rc.1.25451.107", "allowPrerelease": false, "rollForward": "patch" }, "tools": { - "dotnet": "10.0.100-preview.7.25380.108", + "dotnet": "10.0.100-rc.1.25451.107", "vs": { "version": "17.14.0" }, "vswhere": "3.1.7" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25427.2", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25427.2", + "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.25462.2", + "Microsoft.DotNet.Helix.Sdk": "11.0.0-beta.25462.2", "Microsoft.Build.Traversal": "3.4.0" } } diff --git a/src/roslyn/src/Analyzers/CSharp/CodeFixes/AddAnonymousTypeMemberName/CSharpAddAnonymousTypeMemberNameCodeFixProvider.cs b/src/roslyn/src/Analyzers/CSharp/CodeFixes/AddAnonymousTypeMemberName/CSharpAddAnonymousTypeMemberNameCodeFixProvider.cs index 35252af110f..1ff979d1bff 100644 --- a/src/roslyn/src/Analyzers/CSharp/CodeFixes/AddAnonymousTypeMemberName/CSharpAddAnonymousTypeMemberNameCodeFixProvider.cs +++ b/src/roslyn/src/Analyzers/CSharp/CodeFixes/AddAnonymousTypeMemberName/CSharpAddAnonymousTypeMemberNameCodeFixProvider.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.AddAnonymousTypeMemberName; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.AddAnonymousTypeMemberName; @@ -39,5 +40,5 @@ protected override AnonymousObjectMemberDeclaratorSyntax WithName(AnonymousObjec SyntaxFactory.IdentifierName(name))); protected override IEnumerable GetAnonymousObjectMemberNames(AnonymousObjectCreationExpressionSyntax initializer) - => initializer.Initializers.Where(i => i.NameEquals != null).Select(i => i.NameEquals!.Name.Identifier.ValueText); + => initializer.Initializers.SelectAsArray(i => i.NameEquals != null, i => i.NameEquals!.Name.Identifier.ValueText); } diff --git a/src/roslyn/src/Analyzers/CSharp/CodeFixes/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeFixHelper.cs b/src/roslyn/src/Analyzers/CSharp/CodeFixes/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeFixHelper.cs index 0be3c5fc586..9027b048348 100644 --- a/src/roslyn/src/Analyzers/CSharp/CodeFixes/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeFixHelper.cs +++ b/src/roslyn/src/Analyzers/CSharp/CodeFixes/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeFixHelper.cs @@ -98,7 +98,8 @@ public static async Task MakeLocalFunctionStaticAsync( var seenDefaultArgumentValue = currentInvocation.ArgumentList.Arguments.Count < localFunction.ParameterList.Parameters.Count; // Add all the non-this parameters to the end. If there is a 'this' parameter, add it to the start. - var newArguments = parameterAndCapturedSymbols.Where(p => !p.symbol.IsThisParameter()).Select( + var newArguments = parameterAndCapturedSymbols.SelectAsArray( + p => !p.symbol.IsThisParameter(), symbolAndCapture => (ArgumentSyntax)generator.Argument( seenNamedArgument || seenDefaultArgumentValue ? symbolAndCapture.symbol.Name : null, symbolAndCapture.symbol.RefKind, diff --git a/src/roslyn/src/Analyzers/CSharp/CodeFixes/UseImplicitOrExplicitType/UseExplicitTypeCodeFixProvider.cs b/src/roslyn/src/Analyzers/CSharp/CodeFixes/UseImplicitOrExplicitType/UseExplicitTypeCodeFixProvider.cs index fd0d0c27082..82929bb87be 100644 --- a/src/roslyn/src/Analyzers/CSharp/CodeFixes/UseImplicitOrExplicitType/UseExplicitTypeCodeFixProvider.cs +++ b/src/roslyn/src/Analyzers/CSharp/CodeFixes/UseImplicitOrExplicitType/UseExplicitTypeCodeFixProvider.cs @@ -95,7 +95,7 @@ private static async Task HandleDeclarationExpressionAsync(Document document, Sy var tupleTypeSymbol = GetConvertedType(semanticModel, typeSyntax.Parent, cancellationToken); var leadingTrivia = declarationExpression.GetLeadingTrivia() - .Concat(variableDesignation.GetAllPrecedingTriviaToPreviousToken().Where(t => !t.IsWhitespace()).Select(t => t.WithoutAnnotations(SyntaxAnnotation.ElasticAnnotation))); + .Concat(variableDesignation.GetAllPrecedingTriviaToPreviousToken().SelectAsArray(t => !t.IsWhitespace(), t => t.WithoutAnnotations(SyntaxAnnotation.ElasticAnnotation))); var tupleDeclaration = GenerateTupleDeclaration( semanticModel, tupleTypeSymbol, variableDesignation, cancellationToken).WithLeadingTrivia(leadingTrivia); diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/GenerateConstructor/GenerateConstructorTests.cs b/src/roslyn/src/Analyzers/CSharp/Tests/GenerateConstructor/GenerateConstructorTests.cs index 5ddc54f833a..2a4674e2160 100644 --- a/src/roslyn/src/Analyzers/CSharp/Tests/GenerateConstructor/GenerateConstructorTests.cs +++ b/src/roslyn/src/Analyzers/CSharp/Tests/GenerateConstructor/GenerateConstructorTests.cs @@ -25,7 +25,7 @@ public sealed class GenerateConstructorTests(ITestOutputHelper logger) internal override (DiagnosticAnalyzer?, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) => (null, new GenerateConstructorCodeFixProvider()); - private readonly NamingStylesTestOptionSets options = new NamingStylesTestOptionSets(LanguageNames.CSharp); + private readonly NamingStylesTestOptionSets options = new(LanguageNames.CSharp); [Fact] public Task TestWithSimpleArgument() diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/ImplementAbstractClass/ImplementAbstractClassTests_ThroughMember.cs b/src/roslyn/src/Analyzers/CSharp/Tests/ImplementAbstractClass/ImplementAbstractClassTests_ThroughMember.cs index 37a1d66bd08..dd8c167d88a 100644 --- a/src/roslyn/src/Analyzers/CSharp/Tests/ImplementAbstractClass/ImplementAbstractClassTests_ThroughMember.cs +++ b/src/roslyn/src/Analyzers/CSharp/Tests/ImplementAbstractClass/ImplementAbstractClassTests_ThroughMember.cs @@ -24,7 +24,7 @@ internal override (DiagnosticAnalyzer?, CodeFixProvider) CreateDiagnosticProvide => (null, new CSharpImplementAbstractClassCodeFixProvider()); private OptionsCollection AllOptionsOff - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedConstructors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/Nullable/CSharpDeclareAsNullableCodeFixTests.cs b/src/roslyn/src/Analyzers/CSharp/Tests/Nullable/CSharpDeclareAsNullableCodeFixTests.cs index a93373e0cdc..f65576cce4a 100644 --- a/src/roslyn/src/Analyzers/CSharp/Tests/Nullable/CSharpDeclareAsNullableCodeFixTests.cs +++ b/src/roslyn/src/Analyzers/CSharp/Tests/Nullable/CSharpDeclareAsNullableCodeFixTests.cs @@ -21,7 +21,7 @@ public sealed class CSharpDeclareAsNullableCodeFixTests(ITestOutputHelper logger internal override (DiagnosticAnalyzer?, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) => (null, new CSharpDeclareAsNullableCodeFixProvider()); - private static readonly TestParameters s_nullableFeature = new TestParameters(parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8)); + private static readonly TestParameters s_nullableFeature = new(parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8)); [Fact] public Task FixAll() diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/SimplifyInterpolation/SimplifyInterpolationTests.cs b/src/roslyn/src/Analyzers/CSharp/Tests/SimplifyInterpolation/SimplifyInterpolationTests.cs index e0e67a008e3..3cbecc86a5d 100644 --- a/src/roslyn/src/Analyzers/CSharp/Tests/SimplifyInterpolation/SimplifyInterpolationTests.cs +++ b/src/roslyn/src/Analyzers/CSharp/Tests/SimplifyInterpolation/SimplifyInterpolationTests.cs @@ -1056,10 +1056,65 @@ struct TypeNotImplementingIFormattable } """); - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42936")] - public Task ToStringSimplificationIsNotOfferedOnRefStruct() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnRefStructIfInterpolatedStringHandlersUnavailable() + => TestMissingInRegularAndScriptAsync( + """ + class C + { + string M(RefStruct someValue) => $"Test: {someValue[||].ToString()}"; + } + + ref struct RefStruct + { + public override string ToString() => "A"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnReadOnlySpanIfInterpolatedStringHandlersUnavailable() + => TestMissingInRegularAndScriptAsync( + """ + using System; + + namespace System + { + public ref struct ReadOnlySpan { } + } + + class C + { + string M(ReadOnlySpan span) => $"Test: {span[||].ToString()}"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnSpanIfInterpolatedStringHandlersUnavailable() => TestMissingInRegularAndScriptAsync( """ + using System; + + namespace System + { + public ref struct Span { } + public ref struct ReadOnlySpan { } + } + + class C + { + string M(Span span) => $"Test: {span[||].ToString()}"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnRefStructIfInterpolatedStringHandlersAvailable() + => TestMissingInRegularAndScriptAsync( + """ + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + class C { string M(RefStruct someValue) => $"Test: {someValue[||].ToString()}"; @@ -1071,6 +1126,223 @@ ref struct RefStruct } """); + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsOfferedOnReadOnlySpanOfCharIfInterpolatedStringHandlersAvailable() + => TestInRegularAndScriptAsync( + """ + using System; + + namespace System + { + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + string M(ReadOnlySpan span) => $"Test: {span[||].ToString()}"; + } + """, + """ + using System; + + namespace System + { + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + string M(ReadOnlySpan span) => $"Test: {span}"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsOfferedOnSpanOfCharIfInterpolatedStringHandlersAvailable() + => TestInRegularAndScriptAsync( + """ + using System; + + namespace System + { + public ref struct Span { } + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + string M(Span span) => $"Test: {span[||].ToString()}"; + } + """, + """ + using System; + + namespace System + { + public ref struct Span { } + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + string M(Span span) => $"Test: {span}"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnReadOnlySpanOfIntIfInterpolatedStringHandlersAvailable() + => TestMissingInRegularAndScriptAsync( + """ + using System; + + namespace System + { + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + string M(ReadOnlySpan span) => $"Test: {span[||].ToString()}"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnSpanOfIntIfInterpolatedStringHandlersAvailable() + => TestMissingInRegularAndScriptAsync( + """ + using System; + + namespace System + { + public ref struct Span { } + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + string M(Span span) => $"Test: {span[||].ToString()}"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnReadOnlySpanIfTargetsFormattableStringAndInterpolatedStringHandlersAvailable() + => TestMissingInRegularAndScriptAsync( + """ + using System; + + namespace System + { + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + FormattableString M(ReadOnlySpan span) => $"Test: {span[||].ToString()}"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnSpanIfTargetsFormattableStringAndInterpolatedStringHandlersAvailable() + => TestMissingInRegularAndScriptAsync( + """ + using System; + + namespace System + { + public ref struct Span { } + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + FormattableString M(Span span) => $"Test: {span[||].ToString()}"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnReadOnlySpanIfTargetsIFormattableAndInterpolatedStringHandlersAvailable() + => TestMissingInRegularAndScriptAsync( + """ + using System; + + namespace System + { + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + IFormattable M(ReadOnlySpan span) => $"Test: {span[||].ToString()}"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnSpanIfTargetsIFormattableAndInterpolatedStringHandlersAvailable() + => TestMissingInRegularAndScriptAsync( + """ + using System; + + namespace System + { + public ref struct Span { } + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + IFormattable M(Span span) => $"Test: {span[||].ToString()}"; + } + """); + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42936")] public Task PadLeftSimplificationIsStillOfferedOnRefStruct() => TestInRegularAndScriptAsync( @@ -1232,7 +1504,7 @@ public Task TestNotPassedToFormattableString1() """ class C { - void B() => M($"{args[||].Length.ToString()}"); + void B(string[] args) => M($"{args[||].Length.ToString()}"); void M(FormattableString fs) { @@ -1242,6 +1514,32 @@ void M(FormattableString fs) } """); + [Fact] + public Task TestNotPassedToIFormattable() + => TestMissingAsync( + """ + class C : ICustomFormatter, IFormatProvider + { + void B(string[] args) => M($"{args[||].Length.ToString()}"); + + void M(IFormattable fs) + { + fs.ToString(null, formatProvider: this); + } + + object? IFormatProvider.GetFormat(Type? formatType) + { + return formatType == typeof(ICustomFormatter) ? this : null; + } + + string ICustomFormatter.Format(string? format, object? arg, IFormatProvider? formatProvider) + { + Console.WriteLine(arg?.GetType()); + return ""; + } + } + """); + [Theory] [InlineData("DateTime", "ToLongDateString", "D")] [InlineData("DateTime", "ToShortDateString", "d")] diff --git a/src/roslyn/src/Analyzers/Core/Analyzers/IDEDiagnosticIdToOptionMappingHelper.cs b/src/roslyn/src/Analyzers/Core/Analyzers/IDEDiagnosticIdToOptionMappingHelper.cs index e3ae127f772..ab070b6da8c 100644 --- a/src/roslyn/src/Analyzers/Core/Analyzers/IDEDiagnosticIdToOptionMappingHelper.cs +++ b/src/roslyn/src/Analyzers/Core/Analyzers/IDEDiagnosticIdToOptionMappingHelper.cs @@ -31,9 +31,9 @@ public static bool TryGetMappedOptions(string diagnosticId, string language, [No public static bool TryGetMappedFadingOption(string diagnosticId, [NotNullWhen(true)] out PerLanguageOption2? fadingOption) => s_diagnosticIdToFadingOptionMap.TryGetValue(diagnosticId, out fadingOption); - public static bool IsKnownIDEDiagnosticId(string diagnosticId) - => s_diagnosticIdToOptionMap.ContainsKey(diagnosticId) || - s_diagnosticIdToLanguageSpecificOptionsMap.Values.Any(map => map.ContainsKey(diagnosticId)); + public static ImmutableHashSet KnownIDEDiagnosticIds + => [.. s_diagnosticIdToOptionMap.Keys, + .. s_diagnosticIdToLanguageSpecificOptionsMap.Values.SelectMany(map => map.Keys)]; public static void AddOptionMapping(string diagnosticId, ImmutableHashSet options) { diff --git a/src/roslyn/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryAttributeSuppressionsDiagnosticAnalyzer.cs b/src/roslyn/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryAttributeSuppressionsDiagnosticAnalyzer.cs index e8555ba286a..e968035ca61 100644 --- a/src/roslyn/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryAttributeSuppressionsDiagnosticAnalyzer.cs +++ b/src/roslyn/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryAttributeSuppressionsDiagnosticAnalyzer.cs @@ -68,7 +68,7 @@ protected sealed override void InitializeWorker(AnalysisContext context) protected sealed class CompilationAnalyzer(Compilation compilation, INamedTypeSymbol suppressMessageAttributeType) { - private readonly SuppressMessageAttributeState _state = new SuppressMessageAttributeState(compilation, suppressMessageAttributeType); + private readonly SuppressMessageAttributeState _state = new(compilation, suppressMessageAttributeType); public void AnalyzeAssemblyOrModuleAttribute(SyntaxNode attributeSyntax, SemanticModel model, Action reportDiagnostic, CancellationToken cancellationToken) { diff --git a/src/roslyn/src/Analyzers/Core/Analyzers/SimplifyInterpolation/AbstractSimplifyInterpolationDiagnosticAnalyzer.cs b/src/roslyn/src/Analyzers/Core/Analyzers/SimplifyInterpolation/AbstractSimplifyInterpolationDiagnosticAnalyzer.cs index feae0e74b51..b24f3b83f33 100644 --- a/src/roslyn/src/Analyzers/Core/Analyzers/SimplifyInterpolation/AbstractSimplifyInterpolationDiagnosticAnalyzer.cs +++ b/src/roslyn/src/Analyzers/Core/Analyzers/SimplifyInterpolation/AbstractSimplifyInterpolationDiagnosticAnalyzer.cs @@ -36,13 +36,20 @@ protected override void InitializeWorker(AnalysisContext context) { var compilation = context.Compilation; var knownToStringFormats = Helpers.BuildKnownToStringFormatsLookupTable(compilation); - context.RegisterOperationAction(context => AnalyzeInterpolation(context, compilation.FormattableStringType(), knownToStringFormats), OperationKind.Interpolation); + + var readOnlySpanOfCharType = compilation.ReadOnlySpanOfTType()?.Construct(compilation.GetSpecialType(SpecialType.System_Char)); + var handlersAvailable = compilation.InterpolatedStringHandlerAttributeType() != null; + + context.RegisterOperationAction(context => AnalyzeInterpolation(context, compilation.FormattableStringType(), compilation.IFormattableType(), readOnlySpanOfCharType, knownToStringFormats, handlersAvailable), OperationKind.Interpolation); }); private void AnalyzeInterpolation( OperationAnalysisContext context, INamedTypeSymbol? formattableStringType, - ImmutableDictionary knownToStringFormats) + INamedTypeSymbol? iFormattableType, + INamedTypeSymbol? readOnlySpanOfCharType, + ImmutableDictionary knownToStringFormats, + bool handlersAvailable) { var option = context.GetAnalyzerOptions().PreferSimplifiedInterpolation; @@ -55,7 +62,7 @@ private void AnalyzeInterpolation( // Formattable strings can observe the inner types of the arguments passed to them. So we can't safely change // to drop ToString in that. if (interpolation.Parent is IInterpolatedStringOperation { Parent: IConversionOperation { Type: { } convertedType } conversion } && - convertedType.Equals(formattableStringType)) + (convertedType.Equals(formattableStringType) || convertedType.Equals(iFormattableType))) { // One exception to this is calling directly into FormattableString.Invariant. That method has known good // behavior that is fine to continue calling into. @@ -68,7 +75,7 @@ private void AnalyzeInterpolation( } this.Helpers.UnwrapInterpolation( - this.VirtualCharService, this.SyntaxFacts, interpolation, knownToStringFormats, out _, out var alignment, out _, + this.VirtualCharService, this.SyntaxFacts, interpolation, knownToStringFormats, readOnlySpanOfCharType, handlersAvailable, out _, out var alignment, out _, out var formatString, out var unnecessaryLocations); if (alignment == null && formatString == null) diff --git a/src/roslyn/src/Analyzers/Core/Analyzers/SimplifyInterpolation/AbstractSimplifyInterpolationHelpers.cs b/src/roslyn/src/Analyzers/Core/Analyzers/SimplifyInterpolation/AbstractSimplifyInterpolationHelpers.cs index dfe238707ed..a66b3581257 100644 --- a/src/roslyn/src/Analyzers/Core/Analyzers/SimplifyInterpolation/AbstractSimplifyInterpolationHelpers.cs +++ b/src/roslyn/src/Analyzers/Core/Analyzers/SimplifyInterpolation/AbstractSimplifyInterpolationHelpers.cs @@ -65,6 +65,8 @@ public void UnwrapInterpolation( ISyntaxFacts syntaxFacts, IInterpolationOperation interpolation, ImmutableDictionary knownToStringFormats, + INamedTypeSymbol? readOnlySpanOfCharType, + bool handlersAvailable, out TExpressionSyntax? unwrapped, out TExpressionSyntax? alignment, out bool negate, @@ -82,7 +84,7 @@ public void UnwrapInterpolation( UnwrapAlignmentPadding(expression, out expression, out alignment, out negate, unnecessarySpans); if (interpolation.FormatString == null) - UnwrapFormatString(virtualCharService, syntaxFacts, expression, knownToStringFormats, out expression, out formatString, unnecessarySpans); + UnwrapFormatString(virtualCharService, syntaxFacts, expression, knownToStringFormats, readOnlySpanOfCharType, handlersAvailable, out expression, out formatString, unnecessarySpans); unwrapped = GetPreservedInterpolationExpressionSyntax(expression) as TExpressionSyntax; @@ -118,6 +120,8 @@ private void UnwrapFormatString( ISyntaxFacts syntaxFacts, IOperation expression, ImmutableDictionary knownToStringFormats, + INamedTypeSymbol? readOnlySpanOfCharType, + bool handlersAvailable, out IOperation unwrapped, out string? formatString, ArrayBuilder unnecessarySpans) @@ -128,41 +132,43 @@ private void UnwrapFormatString( HasNonImplicitInstance(invocation, out var instance) && !syntaxFacts.IsBaseExpression(instance.Syntax)) { - if (targetMethod.Name == nameof(ToString) && - instance.Type is { IsRefLikeType: false }) + if (targetMethod.Name == nameof(ToString)) { - if (invocation.Arguments.Length == 1 - || (invocation.Arguments.Length == 2 && UsesInvariantCultureReferenceInsideFormattableStringInvariant(invocation, formatProviderArgumentIndex: 1))) + // If type of instance is not ref-like type or is {ReadOnly}Span that is allowed in interpolated strings in .NET 6+ + if (instance.Type is { IsRefLikeType: false } || IsRefLikeTypeAllowed(instance.Type)) { - if (invocation.Arguments[0].Value is ILiteralOperation { ConstantValue: { HasValue: true, Value: string value } } literal && - FindType(expression.SemanticModel) is { } systemIFormattable && - instance.Type.Implements(systemIFormattable)) + if (invocation.Arguments.Length == 1 + || (invocation.Arguments.Length == 2 && UsesInvariantCultureReferenceInsideFormattableStringInvariant(invocation, formatProviderArgumentIndex: 1))) { + if (invocation.Arguments[0].Value is ILiteralOperation { ConstantValue: { HasValue: true, Value: string value } } literal && + FindType(expression.SemanticModel) is { } systemIFormattable && + instance.Type.Implements(systemIFormattable)) + { + unwrapped = instance; + formatString = value; + + unnecessarySpans.AddRange(invocation.Syntax.Span + .Subtract(GetPreservedInterpolationExpressionSyntax(instance).FullSpan) + .Subtract(GetSpanWithinLiteralQuotes(virtualCharService, literal.Syntax.GetFirstToken()))); + return; + } + } + + if (IsObjectToStringOverride(invocation.TargetMethod) + || (invocation.Arguments.Length == 1 && UsesInvariantCultureReferenceInsideFormattableStringInvariant(invocation, formatProviderArgumentIndex: 0))) + { + // A call to `.ToString()` at the end of the interpolation. This is unnecessary. + // Just remove entirely. unwrapped = instance; - formatString = value; + formatString = ""; unnecessarySpans.AddRange(invocation.Syntax.Span - .Subtract(GetPreservedInterpolationExpressionSyntax(instance).FullSpan) - .Subtract(GetSpanWithinLiteralQuotes(virtualCharService, literal.Syntax.GetFirstToken()))); + .Subtract(GetPreservedInterpolationExpressionSyntax(instance).FullSpan)); return; } } - - if (IsObjectToStringOverride(invocation.TargetMethod) - || (invocation.Arguments.Length == 1 && UsesInvariantCultureReferenceInsideFormattableStringInvariant(invocation, formatProviderArgumentIndex: 0))) - { - // A call to `.ToString()` at the end of the interpolation. This is unnecessary. - // Just remove entirely. - unwrapped = instance; - formatString = ""; - - unnecessarySpans.AddRange(invocation.Syntax.Span - .Subtract(GetPreservedInterpolationExpressionSyntax(instance).FullSpan)); - return; - } } - - if (knownToStringFormats.TryGetValue(targetMethod, out var format)) + else if (knownToStringFormats.TryGetValue(targetMethod, out var format)) { // A call to a known ToString-like method, e.g. `DateTime.ToLongDateString()` // We replace this call with predefined format specifier @@ -177,6 +183,13 @@ private void UnwrapFormatString( unwrapped = expression; formatString = null; + + bool IsRefLikeTypeAllowed([NotNullWhen(true)] ITypeSymbol? type) + { + var compilation = expression.SemanticModel.Compilation; + // {ReadOnly}Span is allowed if interpolated string handlers are available in the compilation (.NET 6+) + return handlersAvailable && compilation.HasImplicitConversion(type, readOnlySpanOfCharType); + } } private static bool IsObjectToStringOverride(IMethodSymbol method) diff --git a/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.State.cs b/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.State.cs index dedd78ae4df..9e120dbee99 100644 --- a/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.State.cs +++ b/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.State.cs @@ -234,7 +234,7 @@ await GetParametersAsync( } private TLanguageService GetRequiredLanguageService(string language) where TLanguageService : ILanguageService - => _document.Project.Solution.Workspace.Services.GetExtendedLanguageServices(language).GetRequiredService(); + => _document.Project.Solution.GetRequiredLanguageService(language); private bool ClashesWithExistingConstructor() { diff --git a/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.CodeAction.cs b/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.CodeAction.cs index 51855dbec33..c008fab550a 100644 --- a/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.CodeAction.cs +++ b/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.CodeAction.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; namespace Microsoft.CodeAnalysis.GenerateMember.GenerateEnumMember; @@ -21,7 +22,7 @@ private sealed partial class GenerateEnumMemberCodeAction(Document document, Sta protected override async Task GetChangedDocumentAsync(CancellationToken cancellationToken) { - var languageServices = _document.Project.Solution.Workspace.Services.GetExtendedLanguageServices(_state.TypeToGenerateIn.Language); + var languageServices = _document.Project.Solution.GetExtendedLanguageServices(_state.TypeToGenerateIn.Language); var codeGenerator = languageServices.GetRequiredService(); var semanticFacts = languageServices.GetRequiredService(); diff --git a/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.SignatureInfo.cs b/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.SignatureInfo.cs index 3a0fca8de4c..efe083d293c 100644 --- a/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.SignatureInfo.cs +++ b/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.SignatureInfo.cs @@ -118,9 +118,7 @@ public async ValueTask GenerateMethodAsync( methodKind: State.MethodKind); // Ensure no conflicts between type parameter names and parameter names. - var languageServiceProvider = Document.Project.Solution.Workspace.Services.GetExtendedLanguageServices(State.TypeToGenerateIn.Language); - - var syntaxFacts = languageServiceProvider.GetService(); + var syntaxFacts = Document.Project.Solution.GetLanguageService(State.TypeToGenerateIn.Language); var equalityComparer = syntaxFacts.StringComparer; var reservedParameterNames = DetermineParameterNames(cancellationToken) diff --git a/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.State.cs b/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.State.cs index ce6a1619e54..24c4323b4a1 100644 --- a/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.State.cs +++ b/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.State.cs @@ -82,7 +82,7 @@ protected async Task TryFinishInitializingStateAsync(TService service, Sem .GetMembers(IdentifierToken.ValueText) .OfType(); - var destinationProvider = document.Project.Solution.Workspace.Services.GetExtendedLanguageServices(TypeToGenerateIn.Language); + var destinationProvider = document.Project.Solution.GetExtendedLanguageServices(TypeToGenerateIn.Language); var syntaxFacts = destinationProvider.GetService(); var syntaxFactory = destinationProvider.GetService(); diff --git a/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.cs b/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.cs index 2d502a68a92..378e204090f 100644 --- a/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.cs +++ b/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.GenerateMember.GenerateParameterizedMember; @@ -48,9 +49,7 @@ protected async ValueTask> GetActionsAsync(Document d if (canGenerateAbstractly) result.Add(new GenerateParameterizedMemberCodeAction((TService)this, document, state, isAbstract: true, generateProperty: false)); - var semanticFacts = document.Project.Solution.Workspace.Services - .GetExtendedLanguageServices(state.TypeToGenerateIn.Language) - .GetRequiredService(); + var semanticFacts = document.Project.Solution.GetRequiredLanguageService(state.TypeToGenerateIn.Language); if (semanticFacts.SupportsParameterizedProperties && state.InvocationExpressionOpt != null) diff --git a/src/roslyn/src/Analyzers/Core/CodeFixes/SimplifyInterpolation/AbstractSimplifyInterpolationCodeFixProvider.cs b/src/roslyn/src/Analyzers/Core/CodeFixes/SimplifyInterpolation/AbstractSimplifyInterpolationCodeFixProvider.cs index b5911cb66c3..88f5603c5e8 100644 --- a/src/roslyn/src/Analyzers/Core/CodeFixes/SimplifyInterpolation/AbstractSimplifyInterpolationCodeFixProvider.cs +++ b/src/roslyn/src/Analyzers/Core/CodeFixes/SimplifyInterpolation/AbstractSimplifyInterpolationCodeFixProvider.cs @@ -48,12 +48,16 @@ protected override async Task FixAllAsync( SyntaxEditor editor, CancellationToken cancellationToken) { var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var compilation = semanticModel.Compilation; var generator = editor.Generator; var generatorInternal = document.GetRequiredLanguageService(); var helpers = this.Helpers; var knownToStringFormats = helpers.BuildKnownToStringFormatsLookupTable(semanticModel.Compilation); + var readOnlySpanOfCharType = compilation.ReadOnlySpanOfTType()?.Construct(compilation.GetSpecialType(SpecialType.System_Char)); + var handlersAvailable = compilation.InterpolatedStringHandlerAttributeType() != null; + foreach (var diagnostic in diagnostics) { var node = diagnostic.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken); @@ -64,7 +68,7 @@ protected override async Task FixAllAsync( helpers.UnwrapInterpolation( document.GetRequiredLanguageService(), document.GetRequiredLanguageService(), - interpolation, knownToStringFormats, out var unwrapped, out var alignment, out var negate, out var formatString, out _); + interpolation, knownToStringFormats, readOnlySpanOfCharType, handlersAvailable, out var unwrapped, out var alignment, out var negate, out var formatString, out _); if (unwrapped == null) continue; diff --git a/src/roslyn/src/CodeStyle/CSharp/Tests/AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_OptionHelpers.cs b/src/roslyn/src/CodeStyle/CSharp/Tests/AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_OptionHelpers.cs index bb91cf3e508..efad3c25d9e 100644 --- a/src/roslyn/src/CodeStyle/CSharp/Tests/AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_OptionHelpers.cs +++ b/src/roslyn/src/CodeStyle/CSharp/Tests/AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_OptionHelpers.cs @@ -34,20 +34,20 @@ internal static (OptionKey2, object) SingleOption(PerLanguageOption2 (new OptionKey2(option, language), codeStyle); internal OptionsCollection Option(Option2> option, T enabled, NotificationOption2 notification) - => new OptionsCollection(GetLanguage()) { { option, enabled, notification } }; + => new(GetLanguage()) { { option, enabled, notification } }; internal OptionsCollection Option(Option2> option, CodeStyleOption2 codeStyle) - => new OptionsCollection(GetLanguage()) { { option, codeStyle } }; + => new(GetLanguage()) { { option, codeStyle } }; internal OptionsCollection Option(PerLanguageOption2> option, T enabled, NotificationOption2 notification) - => new OptionsCollection(GetLanguage()) { { option, enabled, notification } }; + => new(GetLanguage()) { { option, enabled, notification } }; internal OptionsCollection Option(PerLanguageOption2> option, CodeStyleOption2 codeStyle) - => new OptionsCollection(GetLanguage()) { { option, codeStyle } }; + => new(GetLanguage()) { { option, codeStyle } }; internal OptionsCollection Option(Option2 option, T value) - => new OptionsCollection(GetLanguage()) { { option, value } }; + => new(GetLanguage()) { { option, value } }; internal OptionsCollection Option(PerLanguageOption2 option, T value) - => new OptionsCollection(GetLanguage()) { { option, value } }; + => new(GetLanguage()) { { option, value } }; } diff --git a/src/roslyn/src/CodeStyle/Core/CodeFixes/Host/Mef/CodeStyleHostLanguageServices.MefHostExportProvider.cs b/src/roslyn/src/CodeStyle/Core/CodeFixes/Host/Mef/CodeStyleHostLanguageServices.MefHostExportProvider.cs index a62af922ab2..d5b5649ba4f 100644 --- a/src/roslyn/src/CodeStyle/Core/CodeFixes/Host/Mef/CodeStyleHostLanguageServices.MefHostExportProvider.cs +++ b/src/roslyn/src/CodeStyle/Core/CodeFixes/Host/Mef/CodeStyleHostLanguageServices.MefHostExportProvider.cs @@ -10,10 +10,8 @@ namespace Microsoft.CodeAnalysis.Host; internal sealed partial class CodeStyleHostLanguageServices : HostLanguageServices { - private static readonly ConditionalWeakTable s_mappedLanguageServices = - new ConditionalWeakTable(); - private static readonly ConditionalWeakTable s_exportProvidersByLanguageCache = - new ConditionalWeakTable(); + private static readonly ConditionalWeakTable s_mappedLanguageServices = new(); + private static readonly ConditionalWeakTable s_exportProvidersByLanguageCache = new(); private readonly HostLanguageServices _hostLanguageServices; private readonly HostLanguageServices _codeStyleLanguageServices; @@ -35,7 +33,7 @@ public static CodeStyleHostLanguageServices GetRequiredMappedCodeStyleLanguageSe => s_mappedLanguageServices.GetValue(hostLanguageServices, Create); private static CodeStyleHostLanguageServices Create(HostLanguageServices hostLanguageServices) - => new CodeStyleHostLanguageServices(hostLanguageServices); + => new(hostLanguageServices); public override HostWorkspaceServices WorkspaceServices => _hostLanguageServices.WorkspaceServices; diff --git a/src/roslyn/src/CodeStyle/Tools/Program.cs b/src/roslyn/src/CodeStyle/Tools/Program.cs index 19979aa8d9c..ef1992aaf70 100644 --- a/src/roslyn/src/CodeStyle/Tools/Program.cs +++ b/src/roslyn/src/CodeStyle/Tools/Program.cs @@ -255,9 +255,9 @@ and an implied numerical option (such as '4') --> <_GlobalAnalyzerConfigFile_MicrosoftCodeAnalysis{language}CodeStyle Condition="'$(_GlobalAnalyzerConfigFileName_MicrosoftCodeAnalysis{language}CodeStyle)' != ''">$(_GlobalAnalyzerConfigDir_MicrosoftCodeAnalysis{language}CodeStyle)\$(_GlobalAnalyzerConfigFileName_MicrosoftCodeAnalysis{language}CodeStyle) - + + ('$(AnalysisLevelStyle)' != '$(AnalysisLevel)' or '$(AnalysisModeStyle)' != '$(AnalysisMode)' or ('$(EffectiveAnalysisLevelStyle)' != '' and $([MSBuild]::VersionGreaterThanOrEquals('$(EffectiveAnalysisLevelStyle)', '11.0'))))"> diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs index bc1173c91df..148146b5054 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.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; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -224,12 +225,6 @@ private ImmutableArray BindExtensionMemberCref(ExtensionMemberCrefSyntax { CheckFeatureAvailability(syntax, MessageID.IDS_FeatureExtensions, diagnostics); - if (containerOpt is not NamedTypeSymbol namedContainer) - { - ambiguityWinner = null; - return ImmutableArray.Empty; - } - int arity = 0; TypeArgumentListSyntax? typeArgumentListSyntax = null; CrefParameterListSyntax? parameters = null; @@ -256,7 +251,7 @@ private ImmutableArray BindExtensionMemberCref(ExtensionMemberCrefSyntax TypeArgumentListSyntax? extensionTypeArguments = syntax.TypeArgumentList; int extensionArity = extensionTypeArguments?.Arguments.Count ?? 0; - ImmutableArray sortedSymbols = computeSortedAndFilteredCrefExtensionMembers(namedContainer, memberName, extensionArity, arity, extensionTypeArguments, diagnostics, syntax); + ImmutableArray sortedSymbols = computeSortedAndFilteredCrefExtensionMembers(containerOpt, memberName, extensionArity, arity, extensionTypeArguments, diagnostics, syntax); if (sortedSymbols.IsDefaultOrEmpty) { @@ -268,7 +263,7 @@ private ImmutableArray BindExtensionMemberCref(ExtensionMemberCrefSyntax return ProcessCrefMemberLookupResults(sortedSymbols, arity, syntax, typeArgumentListSyntax, parameters, out ambiguityWinner, diagnostics); - ImmutableArray computeSortedAndFilteredCrefExtensionMembers(NamedTypeSymbol container, string name, int extensionArity, int arity, TypeArgumentListSyntax? extensionTypeArguments, BindingDiagnosticBag diagnostics, ExtensionMemberCrefSyntax syntax) + ImmutableArray computeSortedAndFilteredCrefExtensionMembers(NamespaceOrTypeSymbol? containerOpt, string name, int extensionArity, int arity, TypeArgumentListSyntax? extensionTypeArguments, BindingDiagnosticBag diagnostics, ExtensionMemberCrefSyntax syntax) { Debug.Assert(name is not null); @@ -295,9 +290,12 @@ ImmutableArray computeSortedAndFilteredCrefExtensionMembers(NamedTypeSym CompoundUseSiteInfo useSiteInfo = this.GetNewCompoundUseSiteInfo(diagnostics); ArrayBuilder? sortedSymbolsBuilder = null; - foreach (var nested in container.GetTypeMembers()) + foreach (var nested in candidateTypes(containerOpt)) { - if (!nested.IsExtension || nested.Arity != extensionArity || nested.ExtensionParameter is null) + if (!nested.IsExtension + || nested.Arity != extensionArity + || nested.ExtensionParameter is null + || nested is not { ContainingType: { ContainingType: null } }) // only consider extension blocks in top-level types { continue; } @@ -364,6 +362,23 @@ ImmutableArray computeSortedAndFilteredCrefExtensionMembers(NamedTypeSym } return sortedSymbolsBuilder.ToImmutableAndFree(); + + ImmutableArray candidateTypes(NamespaceOrTypeSymbol? containerOpt) + { + if (containerOpt is NamedTypeSymbol namedType) + { + return namedType.GetTypeMembers(""); + } + + NamedTypeSymbol? containingType = ContainingType; + if (containingType is null) + { + return []; + } + + NamedTypeSymbol? enclosingType = containingType.IsExtension ? containingType.ContainingType : containingType; + return enclosingType?.GetTypeMembers("") ?? []; + } } } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index edc5a1bdea0..79c30426737 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -748,6 +748,8 @@ private BoundExpression BindMethodGroupInvocation( ImmutableArray originalMethods; LookupResultKind resultKind; ImmutableArray typeArguments; + BoundExpression receiverOpt = methodGroup.ReceiverOpt; + if (resolution.OverloadResolutionResult != null) { originalMethods = GetOriginalMethods(resolution.OverloadResolutionResult); @@ -759,12 +761,25 @@ private BoundExpression BindMethodGroupInvocation( originalMethods = methodGroup.Methods; resultKind = methodGroup.ResultKind; typeArguments = methodGroup.TypeArgumentsOpt; + + if (originalMethods.IsEmpty && methodGroup.LookupSymbolOpt is { }) + { + Debug.Assert(methodGroup.LookupSymbolOpt is not MethodSymbol); + + // Create receiver as BindMemberAccessBadResult does + receiverOpt = new BoundBadExpression( + methodGroup.Syntax, + methodGroup.ResultKind, + [methodGroup.LookupSymbolOpt], + receiverOpt == null ? [] : [receiverOpt], + GetNonMethodMemberType(methodGroup.LookupSymbolOpt)); + } } result = CreateBadCall( syntax, methodName, - methodGroup.ReceiverOpt, + receiverOpt, originalMethods, resultKind, typeArguments, diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/roslyn/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index 1f3ac35d1a2..574d0704b02 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -1261,7 +1261,7 @@ private void GetDisposalInfoForEnumerator(SyntaxNode syntax, ref ForEachEnumerat } } - if (implementsInterface(enumeratorType, isAsync, diagnostics)) + if (implementsInterface(builder.CollectionType, enumeratorType, isAsync, diagnostics)) { builder.NeedsDisposal = true; return; @@ -1281,7 +1281,7 @@ private void GetDisposalInfoForEnumerator(SyntaxNode syntax, ref ForEachEnumerat } } - bool implementsInterface(TypeSymbol enumeratorType, bool isAsync, BindingDiagnosticBag diagnostics) + bool implementsInterface(TypeSymbol collectionType, TypeSymbol enumeratorType, bool isAsync, BindingDiagnosticBag diagnostics) { CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); @@ -1295,7 +1295,8 @@ bool implementsInterface(TypeSymbol enumeratorType, bool isAsync, BindingDiagnos diagnostics.Add(syntax, useSiteInfo); if (needSupportForRefStructInterfaces && - enumeratorType.ContainingModule != Compilation.SourceModule) + enumeratorType.ContainingModule != Compilation.SourceModule && + !LocalRewriter.CanRewriteForEachAsFor(Compilation, syntax, collectionType, out _, out _, BindingDiagnosticBag.Discarded)) { CheckFeatureAvailability(syntax, MessageID.IDS_FeatureRefStructInterfaces, diagnostics); } @@ -1824,7 +1825,8 @@ private bool AllInterfacesContainsIEnumerable( } } - if (implementedIEnumerable is not null && needSupportForRefStructInterfaces && type.ContainingModule != Compilation.SourceModule) + if (implementedIEnumerable is not null && needSupportForRefStructInterfaces && type.ContainingModule != Compilation.SourceModule && + !LocalRewriter.CanRewriteForEachAsFor(Compilation, collectionSyntax, type, out _, out _, BindingDiagnosticBag.Discarded)) { CheckFeatureAvailability(collectionSyntax, MessageID.IDS_FeatureRefStructInterfaces, diagnostics); } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs b/src/roslyn/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs index d34dc9c50ac..0e6feb2437f 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs @@ -1147,7 +1147,7 @@ private void VisitDeconstructionArguments(ArrayBuilder v { var (placeholder, placeholderConversion) = conversion.DeconstructConversionInfo[i]; var underlyingConversion = BoundNode.GetConversion(placeholderConversion, placeholder); - VisitDeconstructionArguments(nestedVariables, syntax, underlyingConversion, right: invocation.Arguments[i + offset]); + VisitDeconstructionArguments(nestedVariables, syntax, underlyingConversion, right: methodInvocationInfo.ArgsOpt[i + offset]); } } } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Binder/WithCrefTypeParametersBinder.cs b/src/roslyn/src/Compilers/CSharp/Portable/Binder/WithCrefTypeParametersBinder.cs index 556e32c5621..fa09e8f2d01 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Binder/WithCrefTypeParametersBinder.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Binder/WithCrefTypeParametersBinder.cs @@ -63,6 +63,7 @@ private MultiDictionary CreateTypeParameterMap() case SyntaxKind.IndexerMemberCref: case SyntaxKind.OperatorMemberCref: case SyntaxKind.ConversionOperatorMemberCref: + case SyntaxKind.ExtensionMemberCref: { AddTypeParameters((MemberCrefSyntax)_crefSyntax, map); break; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/roslyn/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index b1fba6ed796..e3e34c54dcf 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -106,16 +106,22 @@ internal BoundLambda WithInAnonymousFunctionConversion() public TypeWithAnnotations GetInferredReturnType(ref CompoundUseSiteInfo useSiteInfo, out bool inferredFromFunctionType) { // Nullability (and conversions) are ignored. - return GetInferredReturnType(conversions: null, nullableState: null, ref useSiteInfo, out inferredFromFunctionType); + return GetInferredReturnType(conversions: null, nullableState: null, getterNullResilienceData: null, ref useSiteInfo, out inferredFromFunctionType); } /// /// Infer return type. If `nullableState` is non-null, nullability is also inferred and `NullableWalker.Analyze` /// uses that state to set the inferred nullability of variables in the enclosing scope. `conversions` is /// only needed when nullability is inferred. + /// + /// If 'getterNullResilienceData' is non-null, it is propagated down to the child analysis pass, + /// so that it does not attempt to infer the field's nullable annotation, while a parent pass is also attempting to infer that. /// - public TypeWithAnnotations GetInferredReturnType(ConversionsBase? conversions, NullableWalker.VariableState? nullableState, ref CompoundUseSiteInfo useSiteInfo, out bool inferredFromFunctionType) + public TypeWithAnnotations GetInferredReturnType(ConversionsBase? conversions, NullableWalker.VariableState? nullableState, NullableWalker.GetterNullResilienceData? getterNullResilienceData, ref CompoundUseSiteInfo useSiteInfo, out bool inferredFromFunctionType) { + // Cannot pass 'getterNullResilienceData' without also passing 'nullableState'. + Debug.Assert(getterNullResilienceData is null || nullableState is not null); + if (!InferredReturnType.UseSiteDiagnostics.IsEmpty) { useSiteInfo.AddDiagnostics(InferredReturnType.UseSiteDiagnostics); @@ -151,7 +157,8 @@ public TypeWithAnnotations GetInferredReturnType(ConversionsBase? conversions, N diagnostics, delegateInvokeMethodOpt: delegateType?.DelegateInvokeMethod, initialState: nullableState, - returnTypes); + returnTypes, + getterNullResilienceData); diagnostics.Free(); inferredReturnType = InferReturnType(returnTypes, node: this, Binder, delegateType, Symbol.IsAsync, conversions); returnTypes.Free(); @@ -386,6 +393,7 @@ protected override BoundNode VisitExpressionOrPatternWithoutStackGuard(BoundNode internal partial class UnboundLambda { private readonly NullableWalker.VariableState? _nullableState; + private readonly NullableWalker.GetterNullResilienceData? _getterNullResilienceData; public static UnboundLambda Create( CSharpSyntaxNode syntax, @@ -417,16 +425,17 @@ public static UnboundLambda Create( return lambda; } - private UnboundLambda(SyntaxNode syntax, UnboundLambdaState state, FunctionTypeSymbol? functionType, bool withDependencies, NullableWalker.VariableState? nullableState, bool hasErrors) : + private UnboundLambda(SyntaxNode syntax, UnboundLambdaState state, FunctionTypeSymbol? functionType, bool withDependencies, NullableWalker.VariableState? nullableState, NullableWalker.GetterNullResilienceData? getterNullResilienceData, bool hasErrors) : this(syntax, state, functionType, withDependencies, hasErrors) { this._nullableState = nullableState; + this._getterNullResilienceData = getterNullResilienceData; } - internal UnboundLambda WithNullableState(NullableWalker.VariableState nullableState) + internal UnboundLambda WithNullabilityInfo(NullableWalker.VariableState nullableState, NullableWalker.GetterNullResilienceData? getterNullResilienceData) { var data = Data.WithCaching(true); - var lambda = new UnboundLambda(Syntax, data, FunctionType, WithDependencies, nullableState, HasErrors); + var lambda = new UnboundLambda(Syntax, data, FunctionType, WithDependencies, nullableState, getterNullResilienceData, HasErrors); data.SetUnboundLambda(lambda); return lambda; } @@ -439,7 +448,7 @@ internal UnboundLambda WithNoCache() return this; } - var lambda = new UnboundLambda(Syntax, data, FunctionType, WithDependencies, _nullableState, HasErrors); + var lambda = new UnboundLambda(Syntax, data, FunctionType, WithDependencies, _nullableState, _getterNullResilienceData, HasErrors); data.SetUnboundLambda(lambda); return lambda; } @@ -482,7 +491,7 @@ public Binder GetWithParametersBinder(LambdaSymbol lambdaSymbol, Binder binder) public int ParameterCount { get { return Data.ParameterCount; } } public TypeWithAnnotations InferReturnType(ConversionsBase conversions, NamedTypeSymbol delegateType, ref CompoundUseSiteInfo useSiteInfo, out bool inferredFromFunctionType) - => BindForReturnTypeInference(delegateType).GetInferredReturnType(conversions, _nullableState, ref useSiteInfo, out inferredFromFunctionType); + => BindForReturnTypeInference(delegateType).GetInferredReturnType(conversions, _nullableState, _getterNullResilienceData, ref useSiteInfo, out inferredFromFunctionType); public RefKind RefKind(int index) { return Data.RefKind(index); } public ScopedKind DeclaredScope(int index) { return Data.DeclaredScope(index); } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Compiler/ClsComplianceChecker.cs b/src/roslyn/src/Compilers/CSharp/Portable/Compiler/ClsComplianceChecker.cs index ee26ea798f7..7ff548c3126 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Compiler/ClsComplianceChecker.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Compiler/ClsComplianceChecker.cs @@ -529,7 +529,7 @@ private void CheckBaseTypeCompliance(NamedTypeSymbol symbol) else { NamedTypeSymbol baseType = symbol.EnumUnderlyingType ?? symbol.BaseTypeNoUseSiteDiagnostics; // null for interfaces - System.Diagnostics.Debug.Assert((object)baseType != null || symbol.SpecialType == SpecialType.System_Object, "Only object has no base."); + System.Diagnostics.Debug.Assert((object)baseType != null || symbol.SpecialType == SpecialType.System_Object || symbol.IsExtension, "Only object or extension has no base."); if ((object)baseType != null && !IsCompliantType(baseType, symbol)) { // TODO: it would be nice to report this on the base type clause. diff --git a/src/roslyn/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs b/src/roslyn/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs index e6e11d2093a..3eff3de5721 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs @@ -2634,6 +2634,8 @@ protected void CheckAssigned(BoundExpression expr, SyntaxNode node) #nullable enable private void MarkFieldsUsed(TypeSymbol type) { + type = type.OriginalDefinition; + switch (type.TypeKind) { case TypeKind.Array: diff --git a/src/roslyn/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/roslyn/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 91ff699e3d3..27a0784da34 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -43,6 +43,25 @@ internal NullableAnalysisData(int maxRecursionDepth = -1) } } + /// + /// Additional info for getter null resilience analysis. + /// When this value is passed down through 'Analyze()', it means we are in the process of inferring the nullable annotation of 'field'. + /// The 'assumedAnnotation' should be used as the field's nullable annotation for this analysis pass. + /// See https://github.com/dotnet/csharplang/blob/229406aa6dc51c1e37b98e90eb868d979ec6d195/proposals/csharp-14.0/field-keyword.md#nullability-of-the-backing-field + /// + /// Indicates whether the *not-annotated* pass or the *annotated* pass is being performed. + internal readonly struct GetterNullResilienceData(SynthesizedBackingFieldSymbol field, NullableAnnotation assumedAnnotation) + { + public readonly SynthesizedBackingFieldSymbol field = field; + public readonly NullableAnnotation assumedAnnotation = assumedAnnotation; + + public void Deconstruct(out SynthesizedBackingFieldSymbol field, out NullableAnnotation assumedAnnotation) + { + field = this.field; + assumedAnnotation = this.assumedAnnotation; + } + } + /// /// Used to copy variable slots and types from the NullableWalker for the containing method /// or lambda to the NullableWalker created for a nested lambda or local function. @@ -184,7 +203,7 @@ internal string GetDebuggerDisplay() /// Non-null if we are performing the 'null-resilience' analysis of a getter which uses the 'field' keyword. /// In this case, the inferred nullable annotation of the backing field must not be used, as we are currently in the process of inferring it. /// - private readonly (SynthesizedBackingFieldSymbol field, NullableAnnotation assumedAnnotation)? _getterNullResilienceData; + private readonly GetterNullResilienceData? _getterNullResilienceData; /// /// If true, the parameter types and nullability from _delegateInvokeMethod is used for @@ -460,7 +479,7 @@ private NullableWalker( CSharpCompilation compilation, Symbol? symbol, bool useConstructorExitWarnings, - (SynthesizedBackingFieldSymbol field, NullableAnnotation assumedAnnotation)? getterNullResilienceData, + GetterNullResilienceData? getterNullResilienceData, bool useDelegateInvokeParameterTypes, bool useDelegateInvokeReturnType, MethodSymbol? delegateInvokeMethodOpt, @@ -1738,7 +1757,7 @@ internal static void AnalyzeIfNeeded( BoundNode node, SyntaxNode syntax, DiagnosticBag diagnostics, - (SourcePropertyAccessorSymbol getter, SynthesizedBackingFieldSymbol field, NullableAnnotation assumedNullableAnnotation)? getterNullResilienceData = null) + (SourcePropertyAccessorSymbol symbol, GetterNullResilienceData getterNullResilienceData)? symbolAndGetterNullResilienceData = null) { bool requiresAnalysis = true; var compilation = binder.Compilation; @@ -1754,13 +1773,13 @@ internal static void AnalyzeIfNeeded( Analyze( compilation, - symbol: getterNullResilienceData?.getter, + symbol: symbolAndGetterNullResilienceData?.symbol, node, binder, binder.Conversions, diagnostics, useConstructorExitWarnings: false, - getterNullResilienceData is var (_, field, annotation) ? (field, annotation) : null, + getterNullResilienceData: symbolAndGetterNullResilienceData?.getterNullResilienceData, useDelegateInvokeParameterTypes: false, useDelegateInvokeReturnType: false, delegateInvokeMethodOpt: null, @@ -1781,7 +1800,8 @@ internal static void Analyze( DiagnosticBag diagnostics, MethodSymbol? delegateInvokeMethodOpt, VariableState initialState, - ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)>? returnTypesOpt) + ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)>? returnTypesOpt, + GetterNullResilienceData? getterNullResilienceData) { var symbol = lambda.Symbol; var variables = Variables.Create(initialState.Variables).CreateNestedMethodScope(symbol); @@ -1790,7 +1810,7 @@ internal static void Analyze( compilation, symbol, useConstructorExitWarnings: false, - getterNullResilienceData: null, + getterNullResilienceData: getterNullResilienceData, useDelegateInvokeParameterTypes: useDelegateInvokeParameterTypes, useDelegateInvokeReturnType: useDelegateInvokeReturnType, delegateInvokeMethodOpt: delegateInvokeMethodOpt, @@ -1821,7 +1841,7 @@ private static void Analyze( Conversions conversions, DiagnosticBag diagnostics, bool useConstructorExitWarnings, - (SynthesizedBackingFieldSymbol field, NullableAnnotation assumedAnnotation)? getterNullResilienceData, + GetterNullResilienceData? getterNullResilienceData, bool useDelegateInvokeParameterTypes, bool useDelegateInvokeReturnType, MethodSymbol? delegateInvokeMethodOpt, @@ -8493,7 +8513,7 @@ BoundExpression getArgumentForMethodTypeInference(BoundExpression argument, Visi // MethodTypeInferrer must infer nullability for lambdas based on the nullability // from flow analysis rather than the declared nullability. To allow that, we need // to re-bind lambdas in MethodTypeInferrer. - return getUnboundLambda((BoundLambda)argument, GetVariableState(_variables, lambdaState.Value)); + return getUnboundLambda((BoundLambda)argument, GetVariableState(_variables, lambdaState.Value), _getterNullResilienceData); } if (argument.Kind == BoundKind.CollectionExpression) @@ -8539,9 +8559,9 @@ BoundExpression getArgumentForMethodTypeInference(BoundExpression argument, Visi return new BoundExpressionWithNullability(argument.Syntax, argument, argumentType.NullableAnnotation, argumentType.Type); } - static UnboundLambda getUnboundLambda(BoundLambda expr, VariableState variableState) + static UnboundLambda getUnboundLambda(BoundLambda expr, VariableState variableState, GetterNullResilienceData? getterNullResilienceData) { - return expr.UnboundLambda.WithNullableState(variableState); + return expr.UnboundLambda.WithNullabilityInfo(variableState, getterNullResilienceData); } } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/RuntimeAsyncRewriter.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/RuntimeAsyncRewriter.cs index 972b5f14079..1a38c2de037 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/RuntimeAsyncRewriter.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/RuntimeAsyncRewriter.cs @@ -5,7 +5,9 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CSharp; @@ -22,30 +24,78 @@ public static BoundStatement Rewrite( return node; } - // https://github.com/dotnet/roslyn/issues/79763: struct lifting var variablesToHoist = IteratorAndAsyncCaptureWalker.Analyze(compilationState.Compilation, method, node, isRuntimeAsync: true, diagnostics.DiagnosticBag); + var hoistedLocals = ArrayBuilder.GetInstance(); + var factory = new SyntheticBoundNodeFactory(method, node.Syntax, compilationState, diagnostics); + var rewriter = new RuntimeAsyncRewriter(factory, variablesToHoist, hoistedLocals); + var thisStore = hoistThisIfNeeded(rewriter); + var result = (BoundStatement)rewriter.Visit(node); - if (variablesToHoist.Count > 0) + if (thisStore is not null) { - foreach (var variable in variablesToHoist) - { - // Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - diagnostics.Add(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, variable.GetFirstLocation(), method); - } + result = factory.Block(hoistedLocals.ToImmutableAndFree(), + factory.HiddenSequencePoint(), + factory.ExpressionStatement(thisStore), + result); + } + else if (hoistedLocals.Count > 0) + { + result = factory.Block(hoistedLocals.ToImmutableAndFree(), result); + } + else + { + hoistedLocals.Free(); } - var rewriter = new RuntimeAsyncRewriter(new SyntheticBoundNodeFactory(method, node.Syntax, compilationState, diagnostics)); - var result = (BoundStatement)rewriter.Visit(node); return SpillSequenceSpiller.Rewrite(result, method, compilationState, diagnostics); + + static BoundAssignmentOperator? hoistThisIfNeeded(RuntimeAsyncRewriter rewriter) + { + Debug.Assert(rewriter._factory.CurrentFunction is not null); + var thisParameter = rewriter._factory.CurrentFunction.ThisParameter; + if (thisParameter is { Type.IsValueType: true, RefKind: not RefKind.None }) + { + // This is a struct or a type parameter. We need to replace it with a hoisted local to preserve behavior from + // compiler-generated state machines; `this` is a ref, but results are not observable outside of the method. + // We do this regardless of whether `this` is captured to a ref local, because any usage of `ldarg.0` in these + // scenarios is illegal after the first await. We could be more precise and only do this if `this` is actually + // used after the first await, but at the moment we don't feel that is worth the complexity. + var hoistedThis = rewriter._factory.StoreToTemp(rewriter._factory.This(), out BoundAssignmentOperator store, kind: SynthesizedLocalKind.AwaitByRefSpill); + rewriter._hoistedLocals.Add(hoistedThis.LocalSymbol); + rewriter._proxies.Add(thisParameter, new CapturedToExpressionSymbolReplacement(hoistedThis, hoistedSymbols: [], isReusable: true)); + return store; + } + + return null; + } } private readonly SyntheticBoundNodeFactory _factory; private readonly Dictionary _placeholderMap; + private readonly IReadOnlySet _variablesToHoist; + private readonly RefInitializationHoister _refInitializationHoister; + private readonly ArrayBuilder _hoistedLocals; + private readonly Dictionary _proxies = []; - private RuntimeAsyncRewriter(SyntheticBoundNodeFactory factory) + private RuntimeAsyncRewriter(SyntheticBoundNodeFactory factory, IReadOnlySet variablesToHoist, ArrayBuilder hoistedLocals) { + Debug.Assert(factory.CurrentFunction != null); _factory = factory; _placeholderMap = []; + _variablesToHoist = variablesToHoist; + _refInitializationHoister = new RefInitializationHoister(_factory, _factory.CurrentFunction, TypeMap.Empty); + _hoistedLocals = hoistedLocals; + } + + [return: NotNullIfNotNull(nameof(node))] + public override BoundNode? Visit(BoundNode? node) + { + if (node == null) return node; + var oldSyntax = _factory.Syntax; + _factory.Syntax = node.Syntax; + var result = base.Visit(node); + _factory.Syntax = oldSyntax; + return result; } [return: NotNullIfNotNull(nameof(node))] @@ -161,4 +211,117 @@ public override BoundNode VisitAwaitableValuePlaceholder(BoundAwaitableValuePlac { return _placeholderMap[node]; } + + public override BoundNode? VisitAssignmentOperator(BoundAssignmentOperator node) + { + if (node.Left is not BoundLocal leftLocal) + { + return base.VisitAssignmentOperator(node); + } + + BoundExpression visitedRight; + + if (_variablesToHoist.Contains(leftLocal.LocalSymbol) && !_proxies.ContainsKey(leftLocal.LocalSymbol)) + { + Debug.Assert(leftLocal.LocalSymbol.SynthesizedKind == SynthesizedLocalKind.Spill || + (leftLocal.LocalSymbol.SynthesizedKind == SynthesizedLocalKind.ForEachArray && leftLocal.LocalSymbol.Type.HasInlineArrayAttribute(out _) && leftLocal.LocalSymbol.Type.TryGetInlineArrayElementField() is object)); + Debug.Assert(node.IsRef); + visitedRight = VisitExpression(node.Right); + return _refInitializationHoister.HoistRefInitialization( + leftLocal.LocalSymbol, + visitedRight, + _proxies, + createHoistedLocal, + createHoistedAccess, + this, + isRuntimeAsync: true); + } + + var visitedLeftOrProxy = VisitExpression(leftLocal); + visitedRight = VisitExpression(node.Right); + + if (visitedLeftOrProxy is not BoundLocal visitLeftLocal) + { + // Proxy replacement occurred. We need to reassign the proxy into our local as a sequence. + // ref leftLocal = ref proxy; + // leftLocal = visitedRight; + var assignment = _factory.AssignmentExpression(leftLocal, visitedLeftOrProxy, isRef: true); + return _factory.Sequence([assignment], node.Update(leftLocal, visitedRight, node.IsRef, node.Type)); + } + + return node.Update(visitedLeftOrProxy, visitedRight, node.IsRef, node.Type); + + static LocalSymbol createHoistedLocal(TypeSymbol type, RuntimeAsyncRewriter @this, LocalSymbol local) + { + var hoistedLocal = @this._factory.SynthesizedLocal(type, syntax: local.GetDeclaratorSyntax(), kind: SynthesizedLocalKind.AwaitByRefSpill); + @this._hoistedLocals.Add(hoistedLocal); + return hoistedLocal; + } + + static BoundLocal createHoistedAccess(LocalSymbol local, RuntimeAsyncRewriter @this) + => @this._factory.Local(local); + } + + private bool TryReplaceWithProxy(Symbol localOrParameter, SyntaxNode syntax, [NotNullWhen(true)] out BoundNode? replacement) + { + if (_proxies.TryGetValue(localOrParameter, out CapturedSymbolReplacement? proxy)) + { + replacement = proxy.Replacement(syntax, makeFrame: null, this); + return true; + } + + replacement = null; + return false; + } + + public override BoundNode VisitLocal(BoundLocal node) + { + if (TryReplaceWithProxy(node.LocalSymbol, node.Syntax, out BoundNode? replacement)) + { + return replacement; + } + + Debug.Assert(!_variablesToHoist.Contains(node.LocalSymbol)); + return base.VisitLocal(node)!; + } + + public override BoundNode? VisitParameter(BoundParameter node) + { + if (TryReplaceWithProxy(node.ParameterSymbol, node.Syntax, out BoundNode? replacement)) + { + // Currently, the only parameter we expect to be replaced is `this`, which is handled through VisitThisReference. + // Any other ref to a parameter should have either already been hoisted to a local during local rewriting, or should + // be an illegal ref to a parameter across an await. + throw ExceptionUtilities.Unreachable(); + } + + Debug.Assert(!_variablesToHoist.Contains(node.ParameterSymbol)); + return base.VisitParameter(node); + } + + public override BoundNode? VisitThisReference(BoundThisReference node) + { + Debug.Assert(_factory.CurrentFunction is not null); + var thisParameter = this._factory.CurrentFunction.ThisParameter; + if (TryReplaceWithProxy(thisParameter, node.Syntax, out BoundNode? replacement)) + { + return replacement; + } + + Debug.Assert(thisParameter is not { Type.IsValueType: true, RefKind: RefKind.Ref }); + return base.VisitThisReference(node); + } + + public override BoundNode? VisitExpressionStatement(BoundExpressionStatement node) + { + var expr = VisitExpression(node.Expression); + if (expr is null) + { + // Happens when the node is a hoisted expression that has no side effects. + // The generated proxy will have the original content from this node and we can drop it. + return _factory.StatementList(); + } + + return node.Update(expr); + } } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/BoundTreeToDifferentEnclosingContextRewriter.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/BoundTreeToDifferentEnclosingContextRewriter.cs index 07fd38f7fef..0ac7e0bfe8d 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/BoundTreeToDifferentEnclosingContextRewriter.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/BoundTreeToDifferentEnclosingContextRewriter.cs @@ -192,17 +192,50 @@ public override BoundNode VisitAwaitableValuePlaceholder(BoundAwaitableValuePlac { return null; } + if (property.ContainingType.IsAnonymousType) { //at this point we expect that the code is lowered and that getters of anonymous types are accessed - //only via their corresponding get-methods (see VisitMethodSymbol) + //only via their corresponding get-methods, except for properties in expression trees + + // Property of an anonymous type + var newType = (NamedTypeSymbol)TypeMap.SubstituteType(property.ContainingType).AsTypeSymbolOnly(); + if (ReferenceEquals(newType, property.ContainingType)) + { + // Anonymous type symbol was not rewritten + return property; + } + + // get a new property by name + foreach (var member in newType.GetMembers(property.Name)) + { + if (member.Kind == SymbolKind.Property) + { + return (PropertySymbol)member; + } + } + throw ExceptionUtilities.Unreachable(); } + return ((PropertySymbol)property.OriginalDefinition) .AsMember((NamedTypeSymbol)TypeMap.SubstituteType(property.ContainingType).AsTypeSymbolOnly()) ; } + [return: NotNullIfNotNull(nameof(field))] + public override FieldSymbol? VisitFieldSymbol(FieldSymbol? field) + { + if (field is null) + { + return null; + } + + // Field of a regular type + return ((FieldSymbol)field.OriginalDefinition) + .AsMember((NamedTypeSymbol)TypeMap.SubstituteType(field.ContainingType).AsTypeSymbolOnly()); + } + public override BoundNode? VisitMethodDefIndex(BoundMethodDefIndex node) { // Cannot replace a MethodDefIndex's Method/Type with a substituted symbol. diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodBodyRewriter.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodBodyRewriter.cs index 61c5f3ed28e..73573a03a76 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodBodyRewriter.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodBodyRewriter.cs @@ -166,18 +166,6 @@ protected override ImmutableArray VisitDeclaredLocalFunctions(Immu } } - [return: NotNullIfNotNull(nameof(symbol))] - public override FieldSymbol? VisitFieldSymbol(FieldSymbol? symbol) - { - if (symbol is null) - { - return null; - } - - return symbol.OriginalDefinition - .AsMember((NamedTypeSymbol)TypeMap.SubstituteType(symbol.ContainingType).AsTypeSymbolOnly()); - } - public override BoundNode? VisitCall(BoundCall node) { return ExtensionMethodReferenceRewriter.VisitCall(this, node); diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs index 81d153ff7ad..f38f89b6e7a 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs @@ -1219,23 +1219,13 @@ private static ImmutableArray GetEffectiveArgumentRefKinds(ImmutableArr for (int i = 0; i < parameters.Length; i++) { var paramRefKind = parameters[i].RefKind; - if (paramRefKind is RefKind.In or RefKind.RefReadOnlyParameter) + var currentArgRefKind = argumentRefKindsOpt.IsDefault ? RefKind.None : argumentRefKindsOpt[i]; + var effectiveArgRefKind = GetEffectiveRefKind(paramRefKind, currentArgRefKind, parameters[i].Type, comRefKindMismatchPossible: false); + + if (currentArgRefKind != effectiveArgRefKind) { - var argRefKind = argumentRefKindsOpt.IsDefault ? RefKind.None : argumentRefKindsOpt[i]; fillRefKindsBuilder(argumentRefKindsOpt, parameters, ref refKindsBuilder); - refKindsBuilder[i] = argRefKind == RefKind.None ? RefKind.In : RefKindExtensions.StrictIn; - } - else if (paramRefKind == RefKind.Ref) - { - var argRefKind = argumentRefKindsOpt.IsDefault ? RefKind.None : argumentRefKindsOpt[i]; - if (argRefKind == RefKind.None) - { - // Interpolated strings used as interpolated string handlers are allowed to match ref parameters without `ref` - Debug.Assert(parameters[i].Type is NamedTypeSymbol { IsInterpolatedStringHandlerType: true, IsValueType: true }); - - fillRefKindsBuilder(argumentRefKindsOpt, parameters, ref refKindsBuilder); - refKindsBuilder[i] = RefKind.Ref; - } + refKindsBuilder[i] = effectiveArgRefKind; } } @@ -1266,6 +1256,38 @@ static void fillRefKindsBuilder(ImmutableArray argumentRefKindsOpt, Imm } } + internal static RefKind GetEffectiveRefKind(RefKind paramRefKind, RefKind initialArgRefKind, TypeSymbol paramType, bool comRefKindMismatchPossible) + { + // Patch refKinds for arguments that match 'in' or 'ref readonly' parameters to have effective RefKind + // For the purpose of further analysis we will mark the arguments as - + // - In if was originally passed as None and matches an 'in' or 'ref readonly' parameter + // - StrictIn if was originally passed as In or Ref and matches an 'in' or 'ref readonly' parameter + // Here and in the layers after the lowering we only care about None/notNone differences for the arguments + // Except for async stack spilling which needs to know whether arguments were originally passed as "In" and must obey "no copying" rule. + if (paramRefKind is RefKind.In or RefKind.RefReadOnlyParameter) + { + Debug.Assert(initialArgRefKind is RefKind.None or RefKind.In or RefKind.Ref); + return initialArgRefKind == RefKind.None ? RefKind.In : RefKindExtensions.StrictIn; + } + else if (paramRefKind == RefKind.Ref && initialArgRefKind == RefKind.None) + { + // For interpolated string handlers, we allow struct handlers to be passed as ref without a `ref` + // keyword + if (paramType is NamedTypeSymbol { IsInterpolatedStringHandlerType: true, IsValueType: true }) + { + return RefKind.Ref; + } + else + { + // For complex call locations, it's possible that there's a com parameter that allows passing by ref without an explicit ref keyword. This + // is not handled at the local rewriter. + Debug.Assert(comRefKindMismatchPossible); + } + } + + return initialArgRefKind; + } + // temporariesBuilder will be null when factory is null. internal static bool CanSkipRewriting( ImmutableArray rewrittenArguments, @@ -1428,20 +1450,7 @@ private void BuildStoresToTemps( } arguments[p] = StoreArgumentToTempIfNecessary(forceLambdaSpilling, storesToTemps, argument, argRefKind, paramRefKind); - - // Patch refKinds for arguments that match 'in' or 'ref readonly' parameters to have effective RefKind - // For the purpose of further analysis we will mark the arguments as - - // - In if was originally passed as None and matches an 'in' or 'ref readonly' parameter - // - StrictIn if was originally passed as In or Ref and matches an 'in' or 'ref readonly' parameter - // Here and in the layers after the lowering we only care about None/notNone differences for the arguments - // Except for async stack spilling which needs to know whether arguments were originally passed as "In" and must obey "no copying" rule. - if (paramRefKind is RefKind.In or RefKind.RefReadOnlyParameter) - { - Debug.Assert(argRefKind is RefKind.None or RefKind.In or RefKind.Ref); - argRefKind = argRefKind == RefKind.None ? RefKind.In : RefKindExtensions.StrictIn; - } - - refKinds[p] = argRefKind; + refKinds[p] = GetEffectiveRefKind(paramRefKind, argRefKind, parameters[p].Type, comRefKindMismatchPossible: true); } return; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs index 44fe74506b1..6e2467f28d6 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs @@ -69,26 +69,31 @@ public override BoundNode VisitForEachStatement(BoundForEachStatement node) } private bool CanRewriteForEachAsFor(SyntaxNode forEachSyntax, TypeSymbol nodeExpressionType, [NotNullWhen(true)] out MethodSymbol? indexerGet, [NotNullWhen(true)] out MethodSymbol? lengthGet) + { + return CanRewriteForEachAsFor(_compilation, forEachSyntax, nodeExpressionType, out indexerGet, out lengthGet, _diagnostics); + } + + internal static bool CanRewriteForEachAsFor(CSharpCompilation compilation, SyntaxNode forEachSyntax, TypeSymbol nodeExpressionType, [NotNullWhen(true)] out MethodSymbol? indexerGet, [NotNullWhen(true)] out MethodSymbol? lengthGet, BindingDiagnosticBag diagnostics) { lengthGet = indexerGet = null; var origDefinition = nodeExpressionType.OriginalDefinition; if (origDefinition.SpecialType == SpecialType.System_String) { - lengthGet = UnsafeGetSpecialTypeMethod(forEachSyntax, SpecialMember.System_String__Length); - indexerGet = UnsafeGetSpecialTypeMethod(forEachSyntax, SpecialMember.System_String__Chars); + lengthGet = UnsafeGetSpecialTypeMethod(forEachSyntax, SpecialMember.System_String__Length, compilation, diagnostics); + indexerGet = UnsafeGetSpecialTypeMethod(forEachSyntax, SpecialMember.System_String__Chars, compilation, diagnostics); } - else if ((object)origDefinition == this._compilation.GetWellKnownType(WellKnownType.System_Span_T)) + else if ((object)origDefinition == compilation.GetWellKnownType(WellKnownType.System_Span_T)) { var spanType = (NamedTypeSymbol)nodeExpressionType; - lengthGet = (MethodSymbol?)_factory.WellKnownMember(WellKnownMember.System_Span_T__get_Length, isOptional: true)?.SymbolAsMember(spanType); - indexerGet = (MethodSymbol?)_factory.WellKnownMember(WellKnownMember.System_Span_T__get_Item, isOptional: true)?.SymbolAsMember(spanType); + lengthGet = (MethodSymbol?)Binder.GetWellKnownTypeMember(compilation, WellKnownMember.System_Span_T__get_Length, diagnostics, syntax: forEachSyntax, isOptional: true)?.SymbolAsMember(spanType); + indexerGet = (MethodSymbol?)Binder.GetWellKnownTypeMember(compilation, WellKnownMember.System_Span_T__get_Item, diagnostics, syntax: forEachSyntax, isOptional: true)?.SymbolAsMember(spanType); } - else if ((object)origDefinition == this._compilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T)) + else if ((object)origDefinition == compilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T)) { var spanType = (NamedTypeSymbol)nodeExpressionType; - lengthGet = (MethodSymbol?)_factory.WellKnownMember(WellKnownMember.System_ReadOnlySpan_T__get_Length, isOptional: true)?.SymbolAsMember(spanType); - indexerGet = (MethodSymbol?)_factory.WellKnownMember(WellKnownMember.System_ReadOnlySpan_T__get_Item, isOptional: true)?.SymbolAsMember(spanType); + lengthGet = (MethodSymbol?)Binder.GetWellKnownTypeMember(compilation, WellKnownMember.System_ReadOnlySpan_T__get_Length, diagnostics, syntax: forEachSyntax, isOptional: true)?.SymbolAsMember(spanType); + indexerGet = (MethodSymbol?)Binder.GetWellKnownTypeMember(compilation, WellKnownMember.System_ReadOnlySpan_T__get_Item, diagnostics, syntax: forEachSyntax, isOptional: true)?.SymbolAsMember(spanType); } return lengthGet is { } && indexerGet is { }; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs index ec31dbca802..0cb989bc6d6 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs @@ -178,6 +178,7 @@ private MethodSymbol GetOrCreateBaseFunctionWrapper(MethodSymbol methodBeingWrap return wrapper; } + /// Any new usage of this method will need a similar update in private bool TryReplaceWithProxy(Symbol parameterOrLocal, SyntaxNode syntax, [NotNullWhen(true)] out BoundNode? replacement) { if (proxies.TryGetValue(parameterOrLocal, out CapturedSymbolReplacement? proxy)) @@ -349,48 +350,6 @@ public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationE return node.Update(rewrittenArgument, method, node.IsExtensionMethod, node.WasTargetTyped, type); } - [return: NotNullIfNotNull(nameof(property))] - private new PropertySymbol? VisitPropertySymbol(PropertySymbol? property) - { - if (property is null) - { - return null; - } - - if (!property.ContainingType.IsAnonymousType) - { - // Property of a regular type - return ((PropertySymbol)property.OriginalDefinition) - .AsMember((NamedTypeSymbol)TypeMap.SubstituteType(property.ContainingType).AsTypeSymbolOnly()); - } - - // Method of an anonymous type - var newType = (NamedTypeSymbol)TypeMap.SubstituteType(property.ContainingType).AsTypeSymbolOnly(); - if (ReferenceEquals(newType, property.ContainingType)) - { - // Anonymous type symbol was not rewritten - return property; - } - - // get a new property by name - foreach (var member in newType.GetMembers(property.Name)) - { - if (member.Kind == SymbolKind.Property) - { - return (PropertySymbol)member; - } - } - - throw ExceptionUtilities.Unreachable(); - } - - private new FieldSymbol VisitFieldSymbol(FieldSymbol field) - { - // Property of a regular type - return ((FieldSymbol)field.OriginalDefinition) - .AsMember((NamedTypeSymbol)TypeMap.SubstituteType(field.ContainingType).AsTypeSymbolOnly()); - } - public override BoundNode VisitObjectInitializerMember(BoundObjectInitializerMember node) { ImmutableArray arguments = (ImmutableArray)this.VisitList(node.Arguments); diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/CapturedSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/CapturedSymbol.cs index a78955a399c..f7c465b0913 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/CapturedSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/CapturedSymbol.cs @@ -62,16 +62,17 @@ public override BoundExpression Replacement(SyntaxNode node, Func : CapturedSymbolReplacement + where THoistedSymbolType : Symbol { private readonly BoundExpression _replacement; - public readonly ImmutableArray HoistedFields; + public readonly ImmutableArray HoistedSymbols; - public CapturedToExpressionSymbolReplacement(BoundExpression replacement, ImmutableArray hoistedFields, bool isReusable) + public CapturedToExpressionSymbolReplacement(BoundExpression replacement, ImmutableArray hoistedSymbols, bool isReusable) : base(isReusable) { _replacement = replacement; - this.HoistedFields = hoistedFields; + this.HoistedSymbols = hoistedSymbols; } public override BoundExpression Replacement(SyntaxNode node, Func makeFrame, TArg arg) diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs index 3671e459416..6c62a86c384 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs @@ -96,6 +96,8 @@ internal abstract class MethodToStateMachineRewriter : MethodToClassRewriter // Instrumentation related bound nodes: protected BoundBlockInstrumentation? instrumentation; + private readonly RefInitializationHoister _refInitializationHoister; + // new: public MethodToStateMachineRewriter( SyntheticBoundNodeFactory F, @@ -161,6 +163,8 @@ public MethodToStateMachineRewriter( slotAllocatorOpt, firstState: FirstIncreasingResumableState, increasing: true); + + _refInitializationHoister = new RefInitializationHoister(F, OriginalMethod, TypeMap); } #nullable disable @@ -365,7 +369,7 @@ private BoundStatement PossibleIteratorScope(ImmutableArray locals, } else { - foreach (var field in ((CapturedToExpressionSymbolReplacement)proxy).HoistedFields) + foreach (var field in ((CapturedToExpressionSymbolReplacement)proxy).HoistedSymbols) { AddVariableCleanup(variableCleanup, field); @@ -505,226 +509,6 @@ private void FreeReusableHoistedField(StateMachineFieldSymbol field) fields.Add(field); } - private BoundExpression HoistRefInitialization(LocalSymbol local, BoundAssignmentOperator node) - { - Debug.Assert( - local switch - { - TypeSubstitutedLocalSymbol tsl => tsl.UnderlyingLocalSymbol, - _ => local - } is SynthesizedLocal - ); - Debug.Assert(local.SynthesizedKind == SynthesizedLocalKind.Spill || - (local.SynthesizedKind == SynthesizedLocalKind.ForEachArray && local.Type.HasInlineArrayAttribute(out _) && local.Type.TryGetInlineArrayElementField() is object)); - Debug.Assert(local.GetDeclaratorSyntax() != null); -#pragma warning disable format - Debug.Assert(local.SynthesizedKind switch - { - SynthesizedLocalKind.Spill => this.OriginalMethod.IsAsync, - SynthesizedLocalKind.ForEachArray => this.OriginalMethod.IsAsync || this.OriginalMethod.IsIterator, - _ => false - }); -#pragma warning restore format - - var right = (BoundExpression)Visit(node.Right); - - var sideEffects = ArrayBuilder.GetInstance(); - bool needsSacrificialEvaluation = false; - var hoistedFields = ArrayBuilder.GetInstance(); - - SyntaxNode awaitSyntaxOpt; - int syntaxOffset; - if (F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug) - { - awaitSyntaxOpt = local.GetDeclaratorSyntax(); -#pragma warning disable format - Debug.Assert(local.SynthesizedKind switch - { - SynthesizedLocalKind.Spill => awaitSyntaxOpt.IsKind(SyntaxKind.AwaitExpression) || awaitSyntaxOpt.IsKind(SyntaxKind.SwitchExpression), - SynthesizedLocalKind.ForEachArray => awaitSyntaxOpt is CommonForEachStatementSyntax, - _ => false - }); -#pragma warning restore format - syntaxOffset = OriginalMethod.CalculateLocalSyntaxOffset(LambdaUtilities.GetDeclaratorPosition(awaitSyntaxOpt), awaitSyntaxOpt.SyntaxTree); - } - else - { - // These are only used to calculate debug id for ref-spilled variables, - // no need to do so in release build. - awaitSyntaxOpt = null; - syntaxOffset = -1; - } - - var replacement = HoistExpression(right, awaitSyntaxOpt, syntaxOffset, local.RefKind, sideEffects, hoistedFields, ref needsSacrificialEvaluation); - - proxies.Add(local, new CapturedToExpressionSymbolReplacement(replacement, hoistedFields.ToImmutableAndFree(), isReusable: true)); - - if (needsSacrificialEvaluation) - { - var type = TypeMap.SubstituteType(local.Type).Type; - var sacrificialTemp = F.SynthesizedLocal(type, refKind: RefKind.Ref); - Debug.Assert(TypeSymbol.Equals(type, replacement.Type, TypeCompareKind.ConsiderEverything2)); - return F.Sequence(ImmutableArray.Create(sacrificialTemp), sideEffects.ToImmutableAndFree(), F.AssignmentExpression(F.Local(sacrificialTemp), replacement, isRef: true)); - } - - if (sideEffects.Count == 0) - { - sideEffects.Free(); - return null; - } - - var last = sideEffects.Last(); - sideEffects.RemoveLast(); - return F.Sequence(ImmutableArray.Empty, sideEffects.ToImmutableAndFree(), last); - } - - private BoundExpression HoistExpression( - BoundExpression expr, - SyntaxNode awaitSyntaxOpt, - int syntaxOffset, - RefKind refKind, - ArrayBuilder sideEffects, - ArrayBuilder hoistedFields, - ref bool needsSacrificialEvaluation) - { - switch (expr.Kind) - { - case BoundKind.ArrayAccess: - { - var array = (BoundArrayAccess)expr; - BoundExpression expression = HoistExpression(array.Expression, awaitSyntaxOpt, syntaxOffset, RefKind.None, sideEffects, hoistedFields, ref needsSacrificialEvaluation); - var indices = ArrayBuilder.GetInstance(); - foreach (var index in array.Indices) - { - indices.Add(HoistExpression(index, awaitSyntaxOpt, syntaxOffset, RefKind.None, sideEffects, hoistedFields, ref needsSacrificialEvaluation)); - } - - needsSacrificialEvaluation = true; // need to force array index out of bounds exceptions - return array.Update(expression, indices.ToImmutableAndFree(), array.Type); - } - - case BoundKind.FieldAccess: - { - var field = (BoundFieldAccess)expr; - if (field.FieldSymbol.IsStatic) - { - // the address of a static field, and the value of a readonly static field, is stable - if (refKind != RefKind.None || field.FieldSymbol.IsReadOnly) return expr; - goto default; - } - - if (refKind == RefKind.None) - { - goto default; - } - - var isFieldOfStruct = !field.FieldSymbol.ContainingType.IsReferenceType; - - var receiver = HoistExpression(field.ReceiverOpt, awaitSyntaxOpt, syntaxOffset, - isFieldOfStruct ? refKind : RefKind.None, sideEffects, hoistedFields, ref needsSacrificialEvaluation); - if (receiver.Kind != BoundKind.ThisReference && !isFieldOfStruct) - { - needsSacrificialEvaluation = true; // need the null check in field receiver - } - - return F.Field(receiver, field.FieldSymbol); - } - - case BoundKind.ThisReference: - case BoundKind.BaseReference: - case BoundKind.DefaultExpression: - return expr; - - case BoundKind.Call: - var call = (BoundCall)expr; - // NOTE: There are two kinds of 'In' arguments that we may see at this point: - // - `RefKindExtensions.StrictIn` (originally specified with 'in' modifier) - // - `RefKind.In` (specified with no modifiers and matched an 'in' or 'ref readonly' parameter) - // - // It is allowed to spill ordinary `In` arguments by value if reference-preserving spilling is not possible. - // The "strict" ones do not permit implicit copying, so the same situation should result in an error. - if (refKind != RefKind.None && refKind != RefKind.In) - { - Debug.Assert(refKind is RefKindExtensions.StrictIn or RefKind.Ref or RefKind.Out); - Debug.Assert(call.Method.RefKind != RefKind.None); - F.Diagnostics.Add(ErrorCode.ERR_RefReturningCallAndAwait, F.Syntax.Location, call.Method); - } - // method call is not referentially transparent, we can only spill the result value. - refKind = RefKind.None; - goto default; - - case BoundKind.ConditionalOperator: - var conditional = (BoundConditionalOperator)expr; - // NOTE: There are two kinds of 'In' arguments that we may see at this point: - // - `RefKindExtensions.StrictIn` (originally specified with 'in' modifier) - // - `RefKind.In` (specified with no modifiers and matched an 'in' or 'ref readonly' parameter) - // - // It is allowed to spill ordinary `In` arguments by value if reference-preserving spilling is not possible. - // The "strict" ones do not permit implicit copying, so the same situation should result in an error. - if (refKind != RefKind.None && refKind != RefKind.RefReadOnly) - { - Debug.Assert(refKind is RefKindExtensions.StrictIn or RefKind.Ref or RefKind.In); - Debug.Assert(conditional.IsRef); - F.Diagnostics.Add(ErrorCode.ERR_RefConditionalAndAwait, F.Syntax.Location); - } - // conditional expr is not referentially transparent, we can only spill the result value. - refKind = RefKind.None; - goto default; - - default: - if (expr.ConstantValueOpt != null) - { - return expr; - } - - if (refKind != RefKind.None) - { - throw ExceptionUtilities.UnexpectedValue(expr.Kind); - } - - TypeSymbol fieldType = expr.Type; - StateMachineFieldSymbol hoistedField; - if (F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug) - { - const SynthesizedLocalKind kind = SynthesizedLocalKind.AwaitByRefSpill; - - Debug.Assert(awaitSyntaxOpt != null); - - int ordinal = _synthesizedLocalOrdinals.AssignLocalOrdinal(kind, syntaxOffset); - var id = new LocalDebugId(syntaxOffset, ordinal); - - // Editing await expression is not allowed. Thus all spilled fields will be present in the previous state machine. - // However, it may happen that the type changes, in which case we need to allocate a new slot. - int slotIndex; - if (slotAllocator == null || - !slotAllocator.TryGetPreviousHoistedLocalSlotIndex( - awaitSyntaxOpt, - F.ModuleBuilderOpt.Translate(fieldType, awaitSyntaxOpt, Diagnostics.DiagnosticBag), - kind, - id, - Diagnostics.DiagnosticBag, - out slotIndex)) - { - slotIndex = _nextFreeHoistedLocalSlot++; - } - - string fieldName = GeneratedNames.MakeHoistedLocalFieldName(kind, slotIndex); - hoistedField = F.StateMachineField(expr.Type, fieldName, new LocalSlotDebugInfo(kind, id), slotIndex); - _fieldsForCleanup.Add(hoistedField); - } - else - { - hoistedField = GetOrAllocateReusableHoistedField(fieldType, reused: out _); - } - - hoistedFields.Add(hoistedField); - - var replacement = F.Field(F.This(), hoistedField); - sideEffects.Add(F.AssignmentExpression(replacement, expr)); - return replacement; - } - } - #region Visitors public override BoundNode Visit(BoundNode node) @@ -863,7 +647,69 @@ public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) // We have an assignment to a variable that has not yet been assigned a proxy. // So we assign the proxy before translating the assignment. - return HoistRefInitialization(leftLocal, node); + var visitedRight = (BoundExpression)Visit(node.Right); + return _refInitializationHoister.HoistRefInitialization( + leftLocal, + visitedRight, + proxies, + createHoistedSymbol, + createHoistedAccess, + this, + isRuntimeAsync: false); + + static StateMachineFieldSymbol createHoistedSymbol(TypeSymbol type, MethodToStateMachineRewriter @this, LocalSymbol assignedLocal) + { + StateMachineFieldSymbol hoistedSymbol; + + // https://github.com/dotnet/roslyn/issues/79793 - consider whether runtime async will need some of this work for enc + if (@this.F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug) + { + const SynthesizedLocalKind kind = SynthesizedLocalKind.AwaitByRefSpill; + SyntaxNode awaitSyntax = assignedLocal.GetDeclaratorSyntax(); +#pragma warning disable format + Debug.Assert(assignedLocal.SynthesizedKind switch + { + SynthesizedLocalKind.Spill => awaitSyntax.IsKind(SyntaxKind.AwaitExpression) || awaitSyntax.IsKind(SyntaxKind.SwitchExpression), + SynthesizedLocalKind.ForEachArray => awaitSyntax is CommonForEachStatementSyntax, + _ => false + }); +#pragma warning restore format + int syntaxOffset = @this.OriginalMethod.CalculateLocalSyntaxOffset(LambdaUtilities.GetDeclaratorPosition(awaitSyntax), awaitSyntax.SyntaxTree); + + Debug.Assert(awaitSyntax != null); + + int ordinal = @this._synthesizedLocalOrdinals.AssignLocalOrdinal(kind, syntaxOffset); + var id = new LocalDebugId(syntaxOffset, ordinal); + + // Editing await expression is not allowed. Thus all spilled fields will be present in the previous state machine. + // However, it may happen that the type changes, in which case we need to allocate a new slot. + int slotIndex; + if (@this.slotAllocator == null || + !@this.slotAllocator.TryGetPreviousHoistedLocalSlotIndex( + awaitSyntax, + @this.F.ModuleBuilderOpt.Translate(type, awaitSyntax, @this.Diagnostics.DiagnosticBag), + kind, + id, + @this.Diagnostics.DiagnosticBag, + out slotIndex)) + { + slotIndex = @this._nextFreeHoistedLocalSlot++; + } + + string fieldName = GeneratedNames.MakeHoistedLocalFieldName(kind, slotIndex); + hoistedSymbol = @this.F.StateMachineField(type, fieldName, new LocalSlotDebugInfo(kind, id), slotIndex); + @this._fieldsForCleanup.Add(hoistedSymbol); + } + else + { + hoistedSymbol = @this.GetOrAllocateReusableHoistedField(type, out _); + } + + return hoistedSymbol; + } + + static BoundFieldAccess createHoistedAccess(StateMachineFieldSymbol fieldSymbol, MethodToStateMachineRewriter @this) + => @this.F.Field(@this.F.This(), fieldSymbol); } /// diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/RefInitializationHoister.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/RefInitializationHoister.cs new file mode 100644 index 00000000000..47e165a8f0b --- /dev/null +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/RefInitializationHoister.cs @@ -0,0 +1,304 @@ +// 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.Diagnostics; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Microsoft.CodeAnalysis.CSharp; + +/// +/// Helper used during state machine lowering (async / iterator) to "hoist" the initialization of +/// synthesized ref locals whose right-hand side may contain await or otherwise needs to be +/// evaluated before suspension points. +/// +/// A ref local whose initializer contains side effects (invocations, indexing, field dereference with +/// a non-trivial receiver, etc.) must be rewritten so that: +/// +/// All side effects are performed exactly once and in the original left-to-right order. +/// Potential exceptions (null dereference, bounds checks, etc.) occur before the first suspension (e.g. an await inside the method) just as they would have in the original code. +/// The resulting stored reference (the value of the ref local) remains stable across suspension (i.e. subsequent uses of the ref local after rewrites refer to a syntactically simple expression comprised only of the hoisted symbols and stable primitives). +/// +/// +/// +internal class RefInitializationHoister(SyntheticBoundNodeFactory f, MethodSymbol originalMethod, TypeMap typeMap) + where THoistedSymbol : Symbol + where THoistedAccess : BoundExpression +{ + private readonly SyntheticBoundNodeFactory _factory = f; + private readonly MethodSymbol _originalMethod = originalMethod; + private readonly TypeMap _typeMap = typeMap; + private bool _reportedError; + + /// + /// Hoists the right-hand side of a ref local (or similar synthesized local) initialization. + /// + /// Additional context type passed through to creation callbacks. + /// The synthesized local whose initialization is being processed. Must be of a supported synthesized kind. + /// The (already recursively visited by the enclosing rewriter) original right-hand side expression. + /// Dictionary receiving a proxy replacement mapping from to a stable expression assembled from hoisted components. + /// Factory that creates a new hoisted symbol for a sub-expression value of a given . + /// Factory that produces an access expression to a previously created hoisted symbol. + /// Additional context forwarded to the factories (typically state machine specific info). + /// True when performing lowered transformation for runtime-async (MoveNext-like) method body; affects some validation / debug assertions. + /// + /// A sequence expression containing the side-effect assignments (and possibly a sacrificial evaluation) + /// whose value is the final replacement expression used to initialize the ref local, or null + /// if no side effects needed hoisting. + /// + /// + /// On success a entry is added for the local. + /// Subsequent usages of the local are rewritten (elsewhere) to the replacement expression so the + /// state machine no longer needs to track the original local. + /// + internal BoundExpression? HoistRefInitialization( + LocalSymbol local, + BoundExpression visitedRight, + Dictionary proxies, + Func createHoistedSymbol, + Func createHoistedAccess, + TArg arg, + bool isRuntimeAsync) + { + Debug.Assert( + local switch + { + TypeSubstitutedLocalSymbol tsl => tsl.UnderlyingLocalSymbol, + _ => local + } is SynthesizedLocal + ); + Debug.Assert(local.SynthesizedKind == SynthesizedLocalKind.Spill || + (local.SynthesizedKind == SynthesizedLocalKind.ForEachArray && local.Type.HasInlineArrayAttribute(out _) && local.Type.TryGetInlineArrayElementField() is object)); + Debug.Assert(local.GetDeclaratorSyntax() != null); +#pragma warning disable format + Debug.Assert(local.SynthesizedKind switch + { + SynthesizedLocalKind.Spill => this._originalMethod.IsAsync, + SynthesizedLocalKind.ForEachArray => this._originalMethod.IsAsync || this._originalMethod.IsIterator, + _ => false + }); +#pragma warning restore format + + var sideEffects = ArrayBuilder.GetInstance(); + bool needsSacrificialEvaluation = false; + var hoistedSymbols = ArrayBuilder.GetInstance(); + + var replacement = HoistExpression(visitedRight, local, local.RefKind, sideEffects, hoistedSymbols, ref needsSacrificialEvaluation, createHoistedSymbol, createHoistedAccess, arg, isRuntimeAsync, isFieldAccessOfStruct: false); + + proxies.Add(local, new CapturedToExpressionSymbolReplacement(replacement, hoistedSymbols.ToImmutableAndFree(), isReusable: true)); + + if (needsSacrificialEvaluation) + { + var type = _typeMap.SubstituteType(local.Type).Type; + var sacrificialTemp = _factory.SynthesizedLocal(type, refKind: RefKind.Ref); + Debug.Assert(TypeSymbol.Equals(type, replacement.Type, TypeCompareKind.ConsiderEverything2)); + return _factory.Sequence(ImmutableArray.Create(sacrificialTemp), sideEffects.ToImmutableAndFree(), _factory.AssignmentExpression(_factory.Local(sacrificialTemp), replacement, isRef: true)); + } + + if (sideEffects.Count == 0) + { + sideEffects.Free(); + return null; + } + + var last = sideEffects.Last(); + sideEffects.RemoveLast(); + return _factory.Sequence(ImmutableArray.Empty, sideEffects.ToImmutableAndFree(), last); + } + + /// + /// Recursively processes , hoisting side-effecting / non-stable sub-expressions into + /// separate symbols and returning a (syntactically) simpler expression tree composed only of: + /// + /// Original stable nodes (e.g. constants, this, static readonly field access when allowed). + /// Accesses to hoisted symbols created for previous sub-expressions. + /// + /// + /// Additional context argument type. + /// Expression to transform. + /// The local being initialized ultimately (used for diagnostics and to associate hoisted symbols). + /// The desired ref kind of the full expression result (propagated selectively when it impacts legality of hoisting). + /// Builder collecting assignment expressions that realize side effects exactly once. + /// Builder receiving the created hoisted symbols (parallel to order). + /// Flag that is set if the final composed expression must still be evaluated once now (to force exceptions / checks) even though the reference itself is stable. + /// Callback that creates a hoisted symbol. + /// Callback that creates an access to a hoisted symbol. + /// Additional callback context. + /// Indicates we are in runtime-async lowering path; changes certain debug-time invariants. + /// Whether the current expression is a field access whose receiver is a struct (important for reference preservation rules). + /// The replacement (side-effect free / stable) expression that can stand in for after the collected side effects execute. + private BoundExpression HoistExpression( + BoundExpression expr, + LocalSymbol assignedLocal, + RefKind refKind, + ArrayBuilder sideEffects, + ArrayBuilder hoistedSymbols, + ref bool needsSacrificialEvaluation, + Func createHoistedSymbol, + Func createHoistedAccess, + TArg arg, + bool isRuntimeAsync, + bool isFieldAccessOfStruct) + { + switch (expr.Kind) + { + case BoundKind.ArrayAccess: + { + var array = (BoundArrayAccess)expr; + BoundExpression expression = HoistExpression( + array.Expression, + assignedLocal, + refKind: RefKind.None, + sideEffects, + hoistedSymbols, + ref needsSacrificialEvaluation, + createHoistedSymbol, + createHoistedAccess, + arg, + isRuntimeAsync, + isFieldAccessOfStruct: false); + + var indices = ArrayBuilder.GetInstance(); + foreach (var index in array.Indices) + { + indices.Add(HoistExpression( + index, + assignedLocal, + refKind: RefKind.None, + sideEffects, + hoistedSymbols, + ref needsSacrificialEvaluation, + createHoistedSymbol, + createHoistedAccess, + arg, + isRuntimeAsync, + isFieldAccessOfStruct: false)); + } + + needsSacrificialEvaluation = true; // need to force array index out of bounds exceptions + return array.Update(expression, indices.ToImmutableAndFree(), array.Type); + } + + case BoundKind.FieldAccess: + { + var field = (BoundFieldAccess)expr; + if (field.FieldSymbol.IsStatic) + { + // the address of a static field, and the value of a readonly static field, is stable + if (refKind != RefKind.None || field.FieldSymbol.IsReadOnly) return expr; + goto default; + } + Debug.Assert(field.ReceiverOpt != null); + + if (refKind == RefKind.None) + { + goto default; + } + + var isFieldOfStruct = !field.FieldSymbol.ContainingType.IsReferenceType; + + var receiver = HoistExpression( + field.ReceiverOpt, + assignedLocal, + refKind: isFieldOfStruct ? refKind : RefKind.None, + sideEffects, + hoistedSymbols, + ref needsSacrificialEvaluation, + createHoistedSymbol, + createHoistedAccess, + arg, + isRuntimeAsync, + isFieldAccessOfStruct: isFieldOfStruct); + if (receiver.Kind != BoundKind.ThisReference && !isFieldOfStruct) + { + // Make sure that any potential NRE on the receiver happens before the await. + needsSacrificialEvaluation = true; + } + + return _factory.Field(receiver, field.FieldSymbol); + } + + case BoundKind.ThisReference: + case BoundKind.BaseReference: + case BoundKind.DefaultExpression: + return expr; + + case BoundKind.Call: + var call = (BoundCall)expr; + // NOTE: There are two kinds of 'In' arguments that we may see at this point: + // - `RefKindExtensions.StrictIn` (originally specified with 'in' modifier) + // - `RefKind.In` (specified with no modifiers and matched an 'in' or 'ref readonly' parameter) + // + // It is allowed to spill ordinary `In` arguments by value if reference-preserving spilling is not possible. + // The "strict" ones do not permit implicit copying, so the same situation should result in an error. + if (refKind != RefKind.None && refKind != RefKind.In) + { + Debug.Assert(refKind is RefKindExtensions.StrictIn or RefKind.Ref or RefKind.Out); + if (call.Method.RefKind != RefKind.None) + { + _factory.Diagnostics.Add(ErrorCode.ERR_RefReturningCallAndAwait, _factory.Syntax.Location, call.Method); + _reportedError = true; + } + } + // method call is not referentially transparent, we can only spill the result value. + refKind = RefKind.None; + goto default; + + case BoundKind.ConditionalOperator: + var conditional = (BoundConditionalOperator)expr; + // NOTE: There are two kinds of 'In' arguments that we may see at this point: + // - `RefKindExtensions.StrictIn` (originally specified with 'in' modifier) + // - `RefKind.In` (specified with no modifiers and matched an 'in' or 'ref readonly' parameter) + // + // It is allowed to spill ordinary `In` arguments by value if reference-preserving spilling is not possible. + // The "strict" ones do not permit implicit copying, so the same situation should result in an error. + if (refKind != RefKind.None && refKind != RefKind.RefReadOnly) + { + Debug.Assert(refKind is RefKindExtensions.StrictIn or RefKind.Ref or RefKind.In); + Debug.Assert(conditional.IsRef); + _factory.Diagnostics.Add(ErrorCode.ERR_RefConditionalAndAwait, _factory.Syntax.Location); + _reportedError = true; + } + // conditional expr is not referentially transparent, we can only spill the result value. + refKind = RefKind.None; + goto default; + + default: + if (expr.ConstantValueOpt != null) + { + return expr; + } + + if (refKind != RefKind.None) + { + if (isRuntimeAsync) + { + // If an error was reported about ref escaping earlier, there could be illegal ref accesses later in the method, + // so we track that to ensure that we don't see unexpected cases here. + // This is an access to a field of a struct, or parameter or local of a type parameter, both of which happen by reference. + // The receiver should be a non-ref local or parameter. + // This is safe to hoist into a proxy as the original local will be accessed directly. + Debug.Assert(_reportedError || isFieldAccessOfStruct || expr.Type!.IsTypeParameter()); + Debug.Assert(_reportedError || expr is BoundLocal { LocalSymbol.RefKind: RefKind.None } + or BoundParameter { ParameterSymbol.RefKind: RefKind.None }); + } + else + { + throw ExceptionUtilities.UnexpectedValue(expr.Kind); + } + } + + Debug.Assert(expr.Type is not null); + var hoistedSymbol = createHoistedSymbol(expr.Type, arg, assignedLocal); + hoistedSymbols.Add(hoistedSymbol); + + var replacement = createHoistedAccess(hoistedSymbol, arg); + sideEffects.Add(_factory.AssignmentExpression(replacement, expr)); + return replacement; + } + } +} diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/roslyn/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 03c05482213..f154bc7cae4 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -3241,7 +3241,9 @@ private MemberDeclarationSyntax ParseMemberDeclarationCore(SyntaxKind parentKind private bool IsExtensionContainerStart() { - return this.CurrentToken.ContextualKind == SyntaxKind.ExtensionKeyword && IsFeatureEnabled(MessageID.IDS_FeatureExtensions); + // For error recovery, we recognize `extension` followed by `<` even in older language versions + return this.CurrentToken.ContextualKind == SyntaxKind.ExtensionKeyword && + (IsFeatureEnabled(MessageID.IDS_FeatureExtensions) || this.PeekToken(1).Kind == SyntaxKind.LessThanToken); } // if the modifiers do not contain async or replace and the type is the identifier "async" or "replace", then @@ -8313,7 +8315,7 @@ private bool IsPossibleLocalDeclarationStatement(bool isGlobalScriptLevel) return true; } - return IsPossibleFirstTypedIdentifierInLocaDeclarationStatement(isGlobalScriptLevel); + return IsPossibleFirstTypedIdentifierInLocalDeclarationStatement(isGlobalScriptLevel); } private bool IsPossibleScopedKeyword(bool isFunctionPointerParameter) @@ -8322,7 +8324,7 @@ private bool IsPossibleScopedKeyword(bool isFunctionPointerParameter) return ParsePossibleScopedKeyword(isFunctionPointerParameter, isLambdaParameter: false) != null; } - private bool IsPossibleFirstTypedIdentifierInLocaDeclarationStatement(bool isGlobalScriptLevel) + private bool IsPossibleFirstTypedIdentifierInLocalDeclarationStatement(bool isGlobalScriptLevel) { bool? typedIdentifier = IsPossibleTypedIdentifierStart(this.CurrentToken, this.PeekToken(1), allowThisKeyword: false); if (typedIdentifier != null) @@ -8394,11 +8396,22 @@ private bool IsPossibleFirstTypedIdentifierInLocaDeclarationStatement(bool isGlo return true; } - if (st == ScanTypeFlags.NotType || this.CurrentToken.Kind != SyntaxKind.IdentifierToken) + if (st == ScanTypeFlags.NotType) { return false; } + if (this.CurrentToken.Kind != SyntaxKind.IdentifierToken) + { + // In the case of something like: + // List + // if + // we know that we're in an error case, as the following keyword must be the start of a new statement. + // We'd prefer to assume that this is an incomplete local declaration over an expression, as it's more likely + // the user is just in the middle of writing a local declaration, and not an expression. + return st == ScanTypeFlags.GenericTypeOrExpression && (IsDefiniteStatement() || IsTypeDeclarationStart() || IsAccessibilityModifier(CurrentToken.Kind)); + } + // T? and T* might start an expression, we need to parse further to disambiguate: if (isGlobalScriptLevel) { @@ -8457,7 +8470,7 @@ private bool IsPossibleTopLevelUsingLocalDeclarationStatement() EatToken(); } - return IsPossibleFirstTypedIdentifierInLocaDeclarationStatement(isGlobalScriptLevel: false); + return IsPossibleFirstTypedIdentifierInLocalDeclarationStatement(isGlobalScriptLevel: false); } // Looks ahead for a declaration of a field, property or method declaration following a nullable type T?. @@ -8905,17 +8918,16 @@ private PostSkipAction SkipBadStatementListTokens(SyntaxListBuilder + /// Separate pool for assembly symbols as these collections commonly exceed ArrayBuilder's size threshold. + /// + private static readonly ObjectPool> s_symbolPool = new ObjectPool>(() => new ArrayBuilder()); + /// /// The system assembly, which provides primitive types like Object, String, etc., e.g. mscorlib.dll. /// The value is provided by ReferenceManager and must not be modified. For SourceAssemblySymbol, non-missing @@ -948,7 +953,7 @@ internal NamedTypeSymbol GetPrimitiveType(Microsoft.Cci.PrimitiveTypeCode type) Debug.Assert(this is SourceAssemblySymbol, "Never include references for a non-source assembly, because they don't know about aliases."); - var assemblies = ArrayBuilder.GetInstance(); + var assemblies = s_symbolPool.Allocate(); // ignore reference aliases if searching for a type from a specific assembly: if (assemblyOpt != null) @@ -1015,7 +1020,13 @@ internal NamedTypeSymbol GetPrimitiveType(Microsoft.Cci.PrimitiveTypeCode type) result = candidate; } - assemblies.Free(); + assemblies.Clear(); + + // Do not call assemblies.Free, as the ArrayBuilder isn't associated with our pool and even if it were, we don't + // want the default freeing behavior of limiting pooled array size to ArrayBuilder.PooledArrayLengthLimitExclusive. + // Instead, we need to explicitly add this item back to our pool. + s_symbolPool.Free(assemblies); + Debug.Assert(result?.IsErrorType() != true); return result; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/MethodSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/MethodSymbol.cs index a2dd62da743..09eb8d8bc5a 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/MethodSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/MethodSymbol.cs @@ -332,6 +332,21 @@ INamedTypeSymbol IMethodSymbol.AssociatedAnonymousDelegate DllImportData IMethodSymbol.GetDllImportData() => _underlying.GetDllImportData(); +#nullable enable + IMethodSymbol? IMethodSymbol.AssociatedExtensionImplementation + { + get + { + if (!_underlying.IsDefinition || !_underlying.GetIsNewExtensionMember()) + { + return null; + } + + return _underlying.TryGetCorrespondingExtensionImplementationMethod().GetPublicSymbol(); + } + } +#nullable disable + #region ISymbol Members protected override void Accept(SymbolVisitor visitor) diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/NamedTypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/NamedTypeSymbol.cs index 38863cab17c..0091d809d2f 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/NamedTypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/NamedTypeSymbol.cs @@ -202,6 +202,12 @@ UnderlyingNamedTypeSymbol.OriginalDefinition is SourceMemberContainerTypeSymbol INamedTypeSymbol INamedTypeSymbol.NativeIntegerUnderlyingType => UnderlyingNamedTypeSymbol.NativeIntegerUnderlyingType.GetPublicSymbol(); +#nullable enable + bool INamedTypeSymbol.IsExtension => UnderlyingNamedTypeSymbol.IsExtension; + + IParameterSymbol? INamedTypeSymbol.ExtensionParameter => UnderlyingNamedTypeSymbol.ExtensionParameter?.GetPublicSymbol(); +#nullable disable + #region ISymbol Members protected sealed override void Accept(SymbolVisitor visitor) diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs index 4e71714542d..01254ba3f24 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs @@ -47,8 +47,16 @@ private SourceConstructorSymbol( if (syntax.Identifier.ValueText != containingType.Name) { - // This is probably a method declaration with the type missing. - diagnostics.Add(ErrorCode.ERR_MemberNeedsType, location); + if (syntax.Identifier.Text == "extension") + { + bool reported = !MessageID.IDS_FeatureExtensions.CheckFeatureAvailability(diagnostics, syntax); + Debug.Assert(reported); + } + else + { + // This is probably a method declaration with the type missing. + diagnostics.Add(ErrorCode.ERR_MemberNeedsType, location); + } } bool hasAnyBody = syntax.HasAnyBody(); diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index f9cb27bf315..53d26e08435 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -1843,6 +1843,8 @@ internal override IEnumerable GetInstanceFieldsAndEvents() protected void AfterMembersChecks(BindingDiagnosticBag diagnostics) { var compilation = DeclaringCompilation; + var location = GetFirstLocation(); + if (IsInterface) { CheckInterfaceMembers(this.GetMembersAndInitializers().NonTypeMembers, diagnostics); @@ -1850,6 +1852,7 @@ protected void AfterMembersChecks(BindingDiagnosticBag diagnostics) else if (IsExtension) { CheckExtensionMembers(this.GetMembers(), diagnostics); + MessageID.IDS_FeatureExtensions.CheckFeatureAvailability(diagnostics, compilation, location); } CheckMemberNamesDistinctFromType(diagnostics); @@ -1869,8 +1872,6 @@ protected void AfterMembersChecks(BindingDiagnosticBag diagnostics) ReportRequiredMembers(diagnostics); } - var location = GetFirstLocation(); - if (this.IsRefLikeType) { compilation.EnsureIsByRefLikeAttributeExists(diagnostics, location, modifyCompilation: true); diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs index f635e571f4b..4f5b4603fc0 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs @@ -147,6 +147,14 @@ internal bool InfersNullableAnnotation } } + /// + /// Gets the inferred nullable annotation of the backing field, + /// potentially binding and nullable-analyzing the associated get accessor. + /// + /// + /// The for this symbol does not expose this inferred nullable annotation. + /// For that API, the nullable annotation of the associated property is used instead. + /// internal NullableAnnotation GetInferredNullableAnnotation() { if (_inferredNullableAnnotation == (int)NullableAnnotation.Ignored) @@ -217,7 +225,7 @@ private NullableAnnotation ComputeInferredNullableAnnotation() DiagnosticBag nullableAnalyzeAndFilterDiagnostics(NullableAnnotation assumedNullableAnnotation) { var diagnostics = DiagnosticBag.GetInstance(); - NullableWalker.AnalyzeIfNeeded(binder, boundGetAccessor, boundGetAccessor.Syntax, diagnostics, getterNullResilienceData: (getAccessor, _property.BackingField, assumedNullableAnnotation)); + NullableWalker.AnalyzeIfNeeded(binder, boundGetAccessor, boundGetAccessor.Syntax, diagnostics, symbolAndGetterNullResilienceData: (getAccessor, new NullableWalker.GetterNullResilienceData(_property.BackingField, assumedNullableAnnotation))); if (diagnostics.IsEmptyWithoutResolution) { return diagnostics; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs b/src/roslyn/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs index e8001e59e11..9ce1d27ab03 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs @@ -903,7 +903,7 @@ private static bool NeedsSeparator(SyntaxToken token, SyntaxToken next) return true; } - if (IsKeyword(token.Kind())) + if (IsKeyword(token.Kind()) && !token.IsKind(SyntaxKind.ExtensionKeyword)) { if (!next.IsKind(SyntaxKind.ColonToken) && !next.IsKind(SyntaxKind.DotToken) && diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs index bddf43f5220..3ce646e6931 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs @@ -15,6 +15,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { + [CompilerTrait(CompilerFeature.Async)] public class CodeGenAsyncEHTests : EmitMetadataTestBase { private static readonly MetadataReference[] s_asyncRefs = new[] { MscorlibRef_v4_0_30316_17626, SystemRef_v4_0_30319_17929, SystemCoreRef_v4_0_30319_17929 }; diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs index 6b49e879285..435e3bd64c3 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs @@ -31,7 +31,7 @@ internal enum Instruction YieldBreak } - [CompilerTrait(CompilerFeature.AsyncStreams)] + [CompilerTrait(CompilerFeature.AsyncStreams, CompilerFeature.Async)] public class CodeGenAsyncIteratorTests : EmitMetadataTestBase { internal static string ExpectedOutput(string output) diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs index 3bbd16b6f85..95e4fa9f304 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs @@ -16,6 +16,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { + [CompilerTrait(CompilerFeature.Async)] public class CodeGenAsyncLocalsTests : EmitMetadataTestBase { private static readonly MetadataReference[] s_asyncRefs = new[] { MscorlibRef_v4_0_30316_17626, SystemRef_v4_0_30319_17929, SystemCoreRef_v4_0_30319_17929 }; diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs index a822b966564..379636e59b2 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { - [CompilerTrait(CompilerFeature.AsyncMain)] + [CompilerTrait(CompilerFeature.AsyncMain, CompilerFeature.Async)] public class CodeGenAsyncMainTests : EmitMetadataTestBase { [Fact] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMethodBuilderOverrideTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMethodBuilderOverrideTests.cs index fc204d27f2e..2d74fd74e00 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMethodBuilderOverrideTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMethodBuilderOverrideTests.cs @@ -12,6 +12,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { + [CompilerTrait(CompilerFeature.Async)] public class CodeGenAsyncMethodBuilderOverrideTests : EmitMetadataTestBase { private const string AsyncMethodBuilderAttribute = diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs index 8ae68599773..bc5f8f01cdd 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs @@ -14,6 +14,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { + [CompilerTrait(CompilerFeature.Async)] public class CodeGenAsyncSpillTests : EmitMetadataTestBase { public CodeGenAsyncSpillTests() @@ -1119,6 +1120,8 @@ public static async Task F(int[] array) "; var v = CompileAndVerify(source, options: TestOptions.DebugDll); + // https://github.com/dotnet/roslyn/issues/80147 - There's an extra unneeded array + // load before the await that could be removed v.VerifyIL("Test.d__2.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @"{ // Code size 273 (0x111) @@ -1255,64 +1258,66 @@ .locals init (int V_0, }", sequencePoints: "Test+d__2.MoveNext"); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (18,38): error CS9328: Method 'Test.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // H(array[1] += 2, array[3] += await G(), 4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("Test.F(int[])").WithLocation(18, 38) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [F]: Unexpected type on the stack. { Offset = 0x35, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("Test.F(int[])", """ - // { - // // Code size 54 (0x36) - // .maxstack 4 - // .locals init (int& V_0, - // int V_1, - // int V_2, - // int V_3) - // IL_0000: ldarg.0 - // IL_0001: ldc.i4.1 - // IL_0002: ldelema "int" - // IL_0007: dup - // IL_0008: ldind.i4 - // IL_0009: ldc.i4.2 - // IL_000a: add - // IL_000b: dup - // IL_000c: stloc.3 - // IL_000d: stind.i4 - // IL_000e: ldloc.3 - // IL_000f: ldarg.0 - // IL_0010: ldc.i4.3 - // IL_0011: ldelema "int" - // IL_0016: stloc.0 - // IL_0017: ldloc.0 - // IL_0018: ldind.i4 - // IL_0019: stloc.1 - // IL_001a: call "System.Threading.Tasks.Task Test.G()" - // IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0024: stloc.2 - // IL_0025: ldloc.0 - // IL_0026: ldloc.1 - // IL_0027: ldloc.2 - // IL_0028: add - // IL_0029: dup - // IL_002a: stloc.3 - // IL_002b: stind.i4 - // IL_002c: ldloc.3 - // IL_002d: ldc.i4.4 - // IL_002e: call "int Test.H(int, int, int)" - // IL_0033: pop - // IL_0034: ldc.i4.1 - // IL_0035: ret - // } - // """); + var verifier = CompileAndVerify(comp, verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x40, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.F(int[])", """ + { + // Code size 65 (0x41) + .maxstack 4 + .locals init (int[] V_0, + int& V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: ldelema "int" + IL_0007: dup + IL_0008: ldind.i4 + IL_0009: ldc.i4.2 + IL_000a: add + IL_000b: dup + IL_000c: stloc.s V_4 + IL_000e: stind.i4 + IL_000f: ldloc.s V_4 + IL_0011: ldarg.0 + IL_0012: stloc.0 + IL_0013: ldloc.0 + IL_0014: ldc.i4.3 + IL_0015: ldelem.i4 + IL_0016: pop + IL_0017: ldloc.0 + IL_0018: ldc.i4.3 + IL_0019: ldelem.i4 + IL_001a: stloc.2 + IL_001b: call "System.Threading.Tasks.Task Test.G()" + IL_0020: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: stloc.3 + IL_0026: ldloc.0 + IL_0027: ldc.i4.3 + IL_0028: ldelema "int" + IL_002d: stloc.1 + IL_002e: ldloc.1 + IL_002f: ldloc.2 + IL_0030: ldloc.3 + IL_0031: add + IL_0032: dup + IL_0033: stloc.s V_4 + IL_0035: stind.i4 + IL_0036: ldloc.s V_4 + IL_0038: ldc.i4.4 + IL_0039: call "int Test.H(int, int, int)" + IL_003e: pop + IL_003f: ldc.i4.1 + IL_0040: ret + } + """); } [Fact] @@ -1468,164 +1473,180 @@ .locals init (int V_0, }", sequencePoints: "Test+d__2.MoveNext"); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (18,38): error CS9328: Method 'Test.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // H(array[1] += 2, array[3] += await G(), 4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("Test.F(int[])").WithLocation(18, 38) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [F]: Unexpected type on the stack. { Offset = 0x35, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("Test.F(int[])", """ - // { - // // Code size 54 (0x36) - // .maxstack 4 - // .locals init (int& V_0, - // int V_1, - // int V_2, - // int V_3) - // IL_0000: ldarg.0 - // IL_0001: ldc.i4.1 - // IL_0002: ldelema "int" - // IL_0007: dup - // IL_0008: ldind.i4 - // IL_0009: ldc.i4.2 - // IL_000a: add - // IL_000b: dup - // IL_000c: stloc.3 - // IL_000d: stind.i4 - // IL_000e: ldloc.3 - // IL_000f: ldarg.0 - // IL_0010: ldc.i4.3 - // IL_0011: ldelema "int" - // IL_0016: stloc.0 - // IL_0017: ldloc.0 - // IL_0018: ldind.i4 - // IL_0019: stloc.1 - // IL_001a: call "System.Threading.Tasks.Task Test.G()" - // IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0024: stloc.2 - // IL_0025: ldloc.0 - // IL_0026: ldloc.1 - // IL_0027: ldloc.2 - // IL_0028: add - // IL_0029: dup - // IL_002a: stloc.3 - // IL_002b: stind.i4 - // IL_002c: ldloc.3 - // IL_002d: ldc.i4.4 - // IL_002e: call "int Test.H(int, int, int)" - // IL_0033: pop - // IL_0034: ldc.i4.1 - // IL_0035: ret - // } - // """); + var verifier = CompileAndVerify(comp, verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x40, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.F(int[])", """ + { + // Code size 65 (0x41) + .maxstack 4 + .locals init (int[] V_0, + int& V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: ldelema "int" + IL_0007: dup + IL_0008: ldind.i4 + IL_0009: ldc.i4.2 + IL_000a: add + IL_000b: dup + IL_000c: stloc.s V_4 + IL_000e: stind.i4 + IL_000f: ldloc.s V_4 + IL_0011: ldarg.0 + IL_0012: stloc.0 + IL_0013: ldloc.0 + IL_0014: ldc.i4.3 + IL_0015: ldelem.i4 + IL_0016: pop + IL_0017: ldloc.0 + IL_0018: ldc.i4.3 + IL_0019: ldelem.i4 + IL_001a: stloc.2 + IL_001b: call "System.Threading.Tasks.Task Test.G()" + IL_0020: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: stloc.3 + IL_0026: ldloc.0 + IL_0027: ldc.i4.3 + IL_0028: ldelema "int" + IL_002d: stloc.1 + IL_002e: ldloc.1 + IL_002f: ldloc.2 + IL_0030: ldloc.3 + IL_0031: add + IL_0032: dup + IL_0033: stloc.s V_4 + IL_0035: stind.i4 + IL_0036: ldloc.s V_4 + IL_0038: ldc.i4.4 + IL_0039: call "int Test.H(int, int, int)" + IL_003e: pop + IL_003f: ldc.i4.1 + IL_0040: ret + } + """); } [Fact] public void SpillSequencesInConditionalExpression1() { var source = @" +using System; using System.Threading.Tasks; public class Test { public static int H(int a, int b, int c) { + Console.Write($""H{a},{b},{c};""); return a; } - public static Task G() + public static Task G(int i) { - return null; + Console.Write($""G{i}""); + return Task.FromResult(i); } public static async Task F(int[] array) { - H(0, (1 == await G()) ? array[3] += await G() : 1, 4); + H(0, (1 == await G(1)) ? array[3] += await G(2) : 1, 4); return 1; } + + public static async Task Main() + { + await F(new int[4]); + } } "; - CompileAndVerify(source, options: TestOptions.DebugDll); + + var expectedOutput = "G1G2H0,2,4;"; + CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (18,45): error CS9328: Method 'Test.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // H(0, (1 == await G()) ? array[3] += await G() : 1, 4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("Test.F(int[])").WithLocation(18, 45) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [F]: Unexpected type on the stack. { Offset = 0x3c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("Test.F(int[])", """ - // { - // // Code size 61 (0x3d) - // .maxstack 3 - // .locals init (int V_0, - // int V_1, - // int V_2, - // int V_3, - // int V_4) - // IL_0000: call "System.Threading.Tasks.Task Test.G()" - // IL_0005: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_000a: stloc.0 - // IL_000b: ldc.i4.1 - // IL_000c: ldloc.0 - // IL_000d: bne.un.s IL_0030 - // IL_000f: ldarg.0 - // IL_0010: ldc.i4.3 - // IL_0011: ldelema "int" - // IL_0016: dup - // IL_0017: ldind.i4 - // IL_0018: stloc.2 - // IL_0019: call "System.Threading.Tasks.Task Test.G()" - // IL_001e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0023: stloc.3 - // IL_0024: ldloc.2 - // IL_0025: ldloc.3 - // IL_0026: add - // IL_0027: dup - // IL_0028: stloc.s V_4 - // IL_002a: stind.i4 - // IL_002b: ldloc.s V_4 - // IL_002d: stloc.1 - // IL_002e: br.s IL_0032 - // IL_0030: ldc.i4.1 - // IL_0031: stloc.1 - // IL_0032: ldc.i4.0 - // IL_0033: ldloc.1 - // IL_0034: ldc.i4.4 - // IL_0035: call "int Test.H(int, int, int)" - // IL_003a: pop - // IL_003b: ldc.i4.1 - // IL_003c: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x43, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Main]: Return value missing on the stack. { Offset = 0x11 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.F(int[])", """ + { + // Code size 68 (0x44) + .maxstack 3 + .locals init (int V_0, + int V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldc.i4.1 + IL_0001: call "System.Threading.Tasks.Task Test.G(int)" + IL_0006: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000b: stloc.0 + IL_000c: ldc.i4.1 + IL_000d: ldloc.0 + IL_000e: bne.un.s IL_0037 + IL_0010: ldarg.0 + IL_0011: dup + IL_0012: ldc.i4.3 + IL_0013: ldelem.i4 + IL_0014: pop + IL_0015: dup + IL_0016: ldc.i4.3 + IL_0017: ldelem.i4 + IL_0018: stloc.2 + IL_0019: ldc.i4.2 + IL_001a: call "System.Threading.Tasks.Task Test.G(int)" + IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0024: stloc.3 + IL_0025: ldc.i4.3 + IL_0026: ldelema "int" + IL_002b: ldloc.2 + IL_002c: ldloc.3 + IL_002d: add + IL_002e: dup + IL_002f: stloc.s V_4 + IL_0031: stind.i4 + IL_0032: ldloc.s V_4 + IL_0034: stloc.1 + IL_0035: br.s IL_0039 + IL_0037: ldc.i4.1 + IL_0038: stloc.1 + IL_0039: ldc.i4.0 + IL_003a: ldloc.1 + IL_003b: ldc.i4.4 + IL_003c: call "int Test.H(int, int, int)" + IL_0041: pop + IL_0042: ldc.i4.1 + IL_0043: ret + } + """); } [Fact] public void SpillSequencesInNullCoalescingOperator1() { var source = @" +using System; using System.Threading.Tasks; public class C { public static int H(int a, object b, int c) { + Console.Write($""H{a},{b},{c};""); return a; } @@ -1634,19 +1655,27 @@ public static object O(int a) return null; } - public static Task G() + public static Task G(int i) { - return null; + Console.Write($""G{i};""); + return Task.FromResult(i); } public static async Task F(int[] array) { - H(0, O(array[0] += await G()) ?? (array[1] += await G()), 4); + H(0, O(array[0] += await G(1)) ?? (array[1] += await G(2)), 4); return 1; } + + public static async Task Main() + { + await F(new int[4]); + } } "; - CompileAndVerify(source, options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module => + + var expectedOutput = "G1;G2;H0,2,4;"; + CompileAndVerify(source, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All), expectedOutput: expectedOutput, symbolValidator: module => { AssertEx.Equal(new[] { @@ -1661,7 +1690,7 @@ public static async Task F(int[] array) }, module.GetFieldNames("C.d__3")); }); - CompileAndVerify(source, verify: Verification.Passes, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module => + CompileAndVerify(source, verify: Verification.Passes, options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), expectedOutput: expectedOutput, symbolValidator: module => { AssertEx.Equal(new[] { @@ -1681,80 +1710,84 @@ public static async Task F(int[] array) }); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (23,28): error CS9328: Method 'C.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // H(0, O(array[0] += await G()) ?? (array[1] += await G()), 4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("C.F(int[])").WithLocation(23, 28), - // (23,55): error CS9328: Method 'C.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // H(0, O(array[0] += await G()) ?? (array[1] += await G()), 4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("C.F(int[])").WithLocation(23, 55) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [F]: Unexpected type on the stack. { Offset = 0x55, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("C.F(int[])", """ - // { - // // Code size 86 (0x56) - // .maxstack 3 - // .locals init (int V_0, - // int V_1, - // object V_2, - // int V_3, - // int V_4, - // int V_5) - // IL_0000: ldarg.0 - // IL_0001: ldc.i4.0 - // IL_0002: ldelema "int" - // IL_0007: dup - // IL_0008: ldind.i4 - // IL_0009: stloc.0 - // IL_000a: call "System.Threading.Tasks.Task C.G()" - // IL_000f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0014: stloc.1 - // IL_0015: ldloc.0 - // IL_0016: ldloc.1 - // IL_0017: add - // IL_0018: dup - // IL_0019: stloc.3 - // IL_001a: stind.i4 - // IL_001b: ldloc.3 - // IL_001c: call "object C.O(int)" - // IL_0021: stloc.2 - // IL_0022: ldloc.2 - // IL_0023: brtrue.s IL_004b - // IL_0025: ldarg.0 - // IL_0026: ldc.i4.1 - // IL_0027: ldelema "int" - // IL_002c: dup - // IL_002d: ldind.i4 - // IL_002e: stloc.3 - // IL_002f: call "System.Threading.Tasks.Task C.G()" - // IL_0034: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0039: stloc.s V_4 - // IL_003b: ldloc.3 - // IL_003c: ldloc.s V_4 - // IL_003e: add - // IL_003f: dup - // IL_0040: stloc.s V_5 - // IL_0042: stind.i4 - // IL_0043: ldloc.s V_5 - // IL_0045: box "int" - // IL_004a: stloc.2 - // IL_004b: ldc.i4.0 - // IL_004c: ldloc.2 - // IL_004d: ldc.i4.4 - // IL_004e: call "int C.H(int, object, int)" - // IL_0053: pop - // IL_0054: ldc.i4.1 - // IL_0055: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x61, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Main]: Return value missing on the stack. { Offset = 0x11 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.F(int[])", """ + { + // Code size 98 (0x62) + .maxstack 3 + .locals init (int V_0, + int V_1, + object V_2, + int V_3, + int V_4, + int V_5) + IL_0000: ldarg.0 + IL_0001: dup + IL_0002: ldc.i4.0 + IL_0003: ldelem.i4 + IL_0004: pop + IL_0005: dup + IL_0006: ldc.i4.0 + IL_0007: ldelem.i4 + IL_0008: stloc.0 + IL_0009: ldc.i4.1 + IL_000a: call "System.Threading.Tasks.Task C.G(int)" + IL_000f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0014: stloc.1 + IL_0015: ldc.i4.0 + IL_0016: ldelema "int" + IL_001b: ldloc.0 + IL_001c: ldloc.1 + IL_001d: add + IL_001e: dup + IL_001f: stloc.3 + IL_0020: stind.i4 + IL_0021: ldloc.3 + IL_0022: call "object C.O(int)" + IL_0027: stloc.2 + IL_0028: ldloc.2 + IL_0029: brtrue.s IL_0057 + IL_002b: ldarg.0 + IL_002c: dup + IL_002d: ldc.i4.1 + IL_002e: ldelem.i4 + IL_002f: pop + IL_0030: dup + IL_0031: ldc.i4.1 + IL_0032: ldelem.i4 + IL_0033: stloc.3 + IL_0034: ldc.i4.2 + IL_0035: call "System.Threading.Tasks.Task C.G(int)" + IL_003a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003f: stloc.s V_4 + IL_0041: ldc.i4.1 + IL_0042: ldelema "int" + IL_0047: ldloc.3 + IL_0048: ldloc.s V_4 + IL_004a: add + IL_004b: dup + IL_004c: stloc.s V_5 + IL_004e: stind.i4 + IL_004f: ldloc.s V_5 + IL_0051: box "int" + IL_0056: stloc.2 + IL_0057: ldc.i4.0 + IL_0058: ldloc.2 + IL_0059: ldc.i4.4 + IL_005a: call "int C.H(int, object, int)" + IL_005f: pop + IL_0060: ldc.i4.1 + IL_0061: ret + } + """); } [WorkItem(4628, "https://github.com/dotnet/roslyn/issues/4628")] @@ -2061,80 +2094,81 @@ public static async Task F(int[] array) CompileAndVerify(source, options: TestOptions.DebugDll); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (23,28): error CS9328: Method 'Test.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // H(0, B(array[0] += await G()) || B(array[1] += await G()), 4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("Test.F(int[])").WithLocation(23, 28), - // (23,56): error CS9328: Method 'Test.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // H(0, B(array[0] += await G()) || B(array[1] += await G()), 4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("Test.F(int[])").WithLocation(23, 56) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [F]: Unexpected type on the stack. { Offset = 0x55, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("Test.F(int[])", """ - // { - // // Code size 86 (0x56) - // .maxstack 3 - // .locals init (int V_0, - // int V_1, - // bool V_2, - // int V_3, - // int V_4, - // int V_5) - // IL_0000: ldarg.0 - // IL_0001: ldc.i4.0 - // IL_0002: ldelema "int" - // IL_0007: dup - // IL_0008: ldind.i4 - // IL_0009: stloc.0 - // IL_000a: call "System.Threading.Tasks.Task Test.G()" - // IL_000f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0014: stloc.1 - // IL_0015: ldloc.0 - // IL_0016: ldloc.1 - // IL_0017: add - // IL_0018: dup - // IL_0019: stloc.3 - // IL_001a: stind.i4 - // IL_001b: ldloc.3 - // IL_001c: call "bool Test.B(int)" - // IL_0021: stloc.2 - // IL_0022: ldloc.2 - // IL_0023: brtrue.s IL_004b - // IL_0025: ldarg.0 - // IL_0026: ldc.i4.1 - // IL_0027: ldelema "int" - // IL_002c: dup - // IL_002d: ldind.i4 - // IL_002e: stloc.3 - // IL_002f: call "System.Threading.Tasks.Task Test.G()" - // IL_0034: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0039: stloc.s V_4 - // IL_003b: ldloc.3 - // IL_003c: ldloc.s V_4 - // IL_003e: add - // IL_003f: dup - // IL_0040: stloc.s V_5 - // IL_0042: stind.i4 - // IL_0043: ldloc.s V_5 - // IL_0045: call "bool Test.B(int)" - // IL_004a: stloc.2 - // IL_004b: ldc.i4.0 - // IL_004c: ldloc.2 - // IL_004d: ldc.i4.4 - // IL_004e: call "int Test.H(int, bool, int)" - // IL_0053: pop - // IL_0054: ldc.i4.1 - // IL_0055: ret - // } - // """); + var verifier = CompileAndVerify(comp, verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x5f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.F(int[])", """ + { + // Code size 96 (0x60) + .maxstack 3 + .locals init (int V_0, + int V_1, + bool V_2, + int V_3, + int V_4, + int V_5) + IL_0000: ldarg.0 + IL_0001: dup + IL_0002: ldc.i4.0 + IL_0003: ldelem.i4 + IL_0004: pop + IL_0005: dup + IL_0006: ldc.i4.0 + IL_0007: ldelem.i4 + IL_0008: stloc.0 + IL_0009: call "System.Threading.Tasks.Task Test.G()" + IL_000e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0013: stloc.1 + IL_0014: ldc.i4.0 + IL_0015: ldelema "int" + IL_001a: ldloc.0 + IL_001b: ldloc.1 + IL_001c: add + IL_001d: dup + IL_001e: stloc.3 + IL_001f: stind.i4 + IL_0020: ldloc.3 + IL_0021: call "bool Test.B(int)" + IL_0026: stloc.2 + IL_0027: ldloc.2 + IL_0028: brtrue.s IL_0055 + IL_002a: ldarg.0 + IL_002b: dup + IL_002c: ldc.i4.1 + IL_002d: ldelem.i4 + IL_002e: pop + IL_002f: dup + IL_0030: ldc.i4.1 + IL_0031: ldelem.i4 + IL_0032: stloc.3 + IL_0033: call "System.Threading.Tasks.Task Test.G()" + IL_0038: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003d: stloc.s V_4 + IL_003f: ldc.i4.1 + IL_0040: ldelema "int" + IL_0045: ldloc.3 + IL_0046: ldloc.s V_4 + IL_0048: add + IL_0049: dup + IL_004a: stloc.s V_5 + IL_004c: stind.i4 + IL_004d: ldloc.s V_5 + IL_004f: call "bool Test.B(int)" + IL_0054: stloc.2 + IL_0055: ldc.i4.0 + IL_0056: ldloc.2 + IL_0057: ldc.i4.4 + IL_0058: call "int Test.H(int, bool, int)" + IL_005d: pop + IL_005e: ldc.i4.1 + IL_005f: ret + } + """); } [Fact] @@ -2929,81 +2963,80 @@ static void Main() CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (23,23): error CS9328: Method 'TestCase.Run(T)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[0] += await GetVal(4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(4)").WithArguments("TestCase.Run(T)").WithLocation(23, 23) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // [Run]: Return value missing on the stack. { Offset = 0x56 } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run(T)", """ - // { - // // Code size 87 (0x57) - // .maxstack 4 - // .locals init (int V_0, //tests - // int V_1, - // int V_2) - // IL_0000: ldc.i4.0 - // IL_0001: stloc.0 - // .try - // { - // IL_0002: ldc.i4.4 - // IL_0003: newarr "int" - // IL_0008: dup - // IL_0009: ldc.i4.0 - // IL_000a: ldc.i4.4 - // IL_000b: stelem.i4 - // IL_000c: ldloc.0 - // IL_000d: ldc.i4.1 - // IL_000e: add - // IL_000f: stloc.0 - // IL_0010: dup - // IL_0011: ldc.i4.0 - // IL_0012: ldelema "int" - // IL_0017: dup - // IL_0018: ldind.i4 - // IL_0019: stloc.1 - // IL_001a: ldarg.0 - // IL_001b: ldc.i4.4 - // IL_001c: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0021: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0026: stloc.2 - // IL_0027: ldloc.1 - // IL_0028: ldloc.2 - // IL_0029: add - // IL_002a: stind.i4 - // IL_002b: ldc.i4.0 - // IL_002c: ldelem.i4 - // IL_002d: ldc.i4.8 - // IL_002e: bne.un.s IL_003c - // IL_0030: ldsfld "int Driver.Count" - // IL_0035: ldc.i4.1 - // IL_0036: add - // IL_0037: stsfld "int Driver.Count" - // IL_003c: leave.s IL_0056 - // } - // finally - // { - // IL_003e: ldsfld "int Driver.Count" - // IL_0043: ldloc.0 - // IL_0044: sub - // IL_0045: stsfld "int Driver.Result" - // IL_004a: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" - // IL_004f: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_0054: pop - // IL_0055: endfinally - // } - // IL_0056: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x5b } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run(T)", """ + { + // Code size 92 (0x5c) + .maxstack 4 + .locals init (int V_0, //tests + int V_1, + int V_2) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldc.i4.4 + IL_0003: newarr "int" + IL_0008: dup + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.4 + IL_000b: stelem.i4 + IL_000c: ldloc.0 + IL_000d: ldc.i4.1 + IL_000e: add + IL_000f: stloc.0 + IL_0010: dup + IL_0011: dup + IL_0012: ldc.i4.0 + IL_0013: ldelem.i4 + IL_0014: pop + IL_0015: dup + IL_0016: ldc.i4.0 + IL_0017: ldelem.i4 + IL_0018: stloc.1 + IL_0019: ldarg.0 + IL_001a: ldc.i4.4 + IL_001b: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0020: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: stloc.2 + IL_0026: ldc.i4.0 + IL_0027: ldelema "int" + IL_002c: ldloc.1 + IL_002d: ldloc.2 + IL_002e: add + IL_002f: stind.i4 + IL_0030: ldc.i4.0 + IL_0031: ldelem.i4 + IL_0032: ldc.i4.8 + IL_0033: bne.un.s IL_0041 + IL_0035: ldsfld "int Driver.Count" + IL_003a: ldc.i4.1 + IL_003b: add + IL_003c: stsfld "int Driver.Count" + IL_0041: leave.s IL_005b + } + finally + { + IL_0043: ldsfld "int Driver.Count" + IL_0048: ldloc.0 + IL_0049: sub + IL_004a: stsfld "int Driver.Result" + IL_004f: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_0054: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_0059: pop + IL_005a: endfinally + } + IL_005b: ret + } + """); } [Fact] @@ -3128,86 +3161,85 @@ static void Main() CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (22,23): error CS9328: Method 'TestCase.Run(T)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[1] += await (GetVal(arr[0])); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await (GetVal(arr[0]))").WithArguments("TestCase.Run(T)").WithLocation(22, 23) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // [Run]: Return value missing on the stack. { Offset = 0x5a } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run(T)", """ - // { - // // Code size 91 (0x5b) - // .maxstack 4 - // .locals init (int V_0, //tests - // int[] V_1, //arr - // int V_2, - // int V_3) - // IL_0000: ldc.i4.0 - // IL_0001: stloc.0 - // .try - // { - // IL_0002: ldc.i4.4 - // IL_0003: newarr "int" - // IL_0008: dup - // IL_0009: ldc.i4.0 - // IL_000a: ldc.i4.8 - // IL_000b: stelem.i4 - // IL_000c: stloc.1 - // IL_000d: ldloc.0 - // IL_000e: ldc.i4.1 - // IL_000f: add - // IL_0010: stloc.0 - // IL_0011: ldloc.1 - // IL_0012: ldc.i4.1 - // IL_0013: ldelema "int" - // IL_0018: dup - // IL_0019: ldind.i4 - // IL_001a: stloc.2 - // IL_001b: ldarg.0 - // IL_001c: ldloc.1 - // IL_001d: ldc.i4.0 - // IL_001e: ldelem.i4 - // IL_001f: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0024: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0029: stloc.3 - // IL_002a: ldloc.2 - // IL_002b: ldloc.3 - // IL_002c: add - // IL_002d: stind.i4 - // IL_002e: ldloc.1 - // IL_002f: ldc.i4.1 - // IL_0030: ldelem.i4 - // IL_0031: ldc.i4.8 - // IL_0032: bne.un.s IL_0040 - // IL_0034: ldsfld "int Driver.Count" - // IL_0039: ldc.i4.1 - // IL_003a: add - // IL_003b: stsfld "int Driver.Count" - // IL_0040: leave.s IL_005a - // } - // finally - // { - // IL_0042: ldsfld "int Driver.Count" - // IL_0047: ldloc.0 - // IL_0048: sub - // IL_0049: stsfld "int Driver.Result" - // IL_004e: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" - // IL_0053: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_0058: pop - // IL_0059: endfinally - // } - // IL_005a: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x5f } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run(T)", """ + { + // Code size 96 (0x60) + .maxstack 4 + .locals init (int V_0, //tests + int[] V_1, //arr + int V_2, + int V_3) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldc.i4.4 + IL_0003: newarr "int" + IL_0008: dup + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.8 + IL_000b: stelem.i4 + IL_000c: stloc.1 + IL_000d: ldloc.0 + IL_000e: ldc.i4.1 + IL_000f: add + IL_0010: stloc.0 + IL_0011: ldloc.1 + IL_0012: dup + IL_0013: ldc.i4.1 + IL_0014: ldelem.i4 + IL_0015: pop + IL_0016: dup + IL_0017: ldc.i4.1 + IL_0018: ldelem.i4 + IL_0019: stloc.2 + IL_001a: ldarg.0 + IL_001b: ldloc.1 + IL_001c: ldc.i4.0 + IL_001d: ldelem.i4 + IL_001e: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0023: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0028: stloc.3 + IL_0029: ldc.i4.1 + IL_002a: ldelema "int" + IL_002f: ldloc.2 + IL_0030: ldloc.3 + IL_0031: add + IL_0032: stind.i4 + IL_0033: ldloc.1 + IL_0034: ldc.i4.1 + IL_0035: ldelem.i4 + IL_0036: ldc.i4.8 + IL_0037: bne.un.s IL_0045 + IL_0039: ldsfld "int Driver.Count" + IL_003e: ldc.i4.1 + IL_003f: add + IL_0040: stsfld "int Driver.Count" + IL_0045: leave.s IL_005f + } + finally + { + IL_0047: ldsfld "int Driver.Count" + IL_004c: ldloc.0 + IL_004d: sub + IL_004e: stsfld "int Driver.Result" + IL_0053: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_0058: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_005d: pop + IL_005e: endfinally + } + IL_005f: ret + } + """); } [Fact] @@ -3342,127 +3374,126 @@ static void Main() CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (22,23): error CS9328: Method 'TestCase.Run(T)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[1] += await (GetVal(arr[await GetVal(0)])); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await (GetVal(arr[await GetVal(0)]))").WithArguments("TestCase.Run(T)").WithLocation(22, 23) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // [Run]: Return value missing on the stack. { Offset = 0xa3 } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run(T)", """ - // { - // // Code size 164 (0xa4) - // .maxstack 4 - // .locals init (int V_0, //tests - // int& V_1, - // int V_2, - // int V_3, - // int[] V_4, - // int V_5) - // IL_0000: ldc.i4.0 - // IL_0001: stloc.0 - // .try - // { - // IL_0002: ldc.i4.4 - // IL_0003: newarr "int" - // IL_0008: dup - // IL_0009: ldc.i4.0 - // IL_000a: ldc.i4.8 - // IL_000b: stelem.i4 - // IL_000c: dup - // IL_000d: ldc.i4.1 - // IL_000e: ldc.i4.8 - // IL_000f: stelem.i4 - // IL_0010: ldloc.0 - // IL_0011: ldc.i4.1 - // IL_0012: add - // IL_0013: stloc.0 - // IL_0014: dup - // IL_0015: ldc.i4.1 - // IL_0016: ldelema "int" - // IL_001b: stloc.1 - // IL_001c: ldloc.1 - // IL_001d: ldind.i4 - // IL_001e: stloc.2 - // IL_001f: dup - // IL_0020: stloc.s V_4 - // IL_0022: ldarg.0 - // IL_0023: ldc.i4.0 - // IL_0024: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0029: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_002e: stloc.s V_5 - // IL_0030: ldarg.0 - // IL_0031: ldloc.s V_4 - // IL_0033: ldloc.s V_5 - // IL_0035: ldelem.i4 - // IL_0036: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_003b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0040: stloc.3 - // IL_0041: ldloc.1 - // IL_0042: ldloc.2 - // IL_0043: ldloc.3 - // IL_0044: add - // IL_0045: stind.i4 - // IL_0046: dup - // IL_0047: ldc.i4.1 - // IL_0048: ldelem.i4 - // IL_0049: ldc.i4.s 16 - // IL_004b: bne.un.s IL_0059 - // IL_004d: ldsfld "int Driver.Count" - // IL_0052: ldc.i4.1 - // IL_0053: add - // IL_0054: stsfld "int Driver.Count" - // IL_0059: ldloc.0 - // IL_005a: ldc.i4.1 - // IL_005b: add - // IL_005c: stloc.0 - // IL_005d: dup - // IL_005e: ldarg.0 - // IL_005f: ldc.i4.2 - // IL_0060: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0065: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_006a: stloc.3 - // IL_006b: ldloc.3 - // IL_006c: ldelema "int" - // IL_0071: dup - // IL_0072: ldind.i4 - // IL_0073: stloc.2 - // IL_0074: ldloc.2 - // IL_0075: ldc.i4.1 - // IL_0076: add - // IL_0077: stind.i4 - // IL_0078: ldc.i4.2 - // IL_0079: ldelem.i4 - // IL_007a: ldc.i4.1 - // IL_007b: bne.un.s IL_0089 - // IL_007d: ldsfld "int Driver.Count" - // IL_0082: ldc.i4.1 - // IL_0083: add - // IL_0084: stsfld "int Driver.Count" - // IL_0089: leave.s IL_00a3 - // } - // finally - // { - // IL_008b: ldsfld "int Driver.Count" - // IL_0090: ldloc.0 - // IL_0091: sub - // IL_0092: stsfld "int Driver.Result" - // IL_0097: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" - // IL_009c: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_00a1: pop - // IL_00a2: endfinally - // } - // IL_00a3: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0xa8 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run(T)", """ + { + // Code size 169 (0xa9) + .maxstack 4 + .locals init (int[] V_0, + int V_1, //tests + int V_2, + int V_3, + int[] V_4, + int V_5) + IL_0000: ldc.i4.0 + IL_0001: stloc.1 + .try + { + IL_0002: ldc.i4.4 + IL_0003: newarr "int" + IL_0008: dup + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.8 + IL_000b: stelem.i4 + IL_000c: dup + IL_000d: ldc.i4.1 + IL_000e: ldc.i4.8 + IL_000f: stelem.i4 + IL_0010: ldloc.1 + IL_0011: ldc.i4.1 + IL_0012: add + IL_0013: stloc.1 + IL_0014: dup + IL_0015: stloc.0 + IL_0016: ldloc.0 + IL_0017: ldc.i4.1 + IL_0018: ldelem.i4 + IL_0019: pop + IL_001a: ldloc.0 + IL_001b: ldc.i4.1 + IL_001c: ldelem.i4 + IL_001d: stloc.2 + IL_001e: dup + IL_001f: stloc.s V_4 + IL_0021: ldarg.0 + IL_0022: ldc.i4.0 + IL_0023: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0028: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002d: stloc.s V_5 + IL_002f: ldarg.0 + IL_0030: ldloc.s V_4 + IL_0032: ldloc.s V_5 + IL_0034: ldelem.i4 + IL_0035: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_003a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003f: stloc.3 + IL_0040: ldloc.0 + IL_0041: ldc.i4.1 + IL_0042: ldelema "int" + IL_0047: ldloc.2 + IL_0048: ldloc.3 + IL_0049: add + IL_004a: stind.i4 + IL_004b: dup + IL_004c: ldc.i4.1 + IL_004d: ldelem.i4 + IL_004e: ldc.i4.s 16 + IL_0050: bne.un.s IL_005e + IL_0052: ldsfld "int Driver.Count" + IL_0057: ldc.i4.1 + IL_0058: add + IL_0059: stsfld "int Driver.Count" + IL_005e: ldloc.1 + IL_005f: ldc.i4.1 + IL_0060: add + IL_0061: stloc.1 + IL_0062: dup + IL_0063: ldarg.0 + IL_0064: ldc.i4.2 + IL_0065: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_006a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_006f: stloc.3 + IL_0070: ldloc.3 + IL_0071: ldelema "int" + IL_0076: dup + IL_0077: ldind.i4 + IL_0078: stloc.2 + IL_0079: ldloc.2 + IL_007a: ldc.i4.1 + IL_007b: add + IL_007c: stind.i4 + IL_007d: ldc.i4.2 + IL_007e: ldelem.i4 + IL_007f: ldc.i4.1 + IL_0080: bne.un.s IL_008e + IL_0082: ldsfld "int Driver.Count" + IL_0087: ldc.i4.1 + IL_0088: add + IL_0089: stsfld "int Driver.Count" + IL_008e: leave.s IL_00a8 + } + finally + { + IL_0090: ldsfld "int Driver.Count" + IL_0095: ldloc.1 + IL_0096: sub + IL_0097: stsfld "int Driver.Result" + IL_009c: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_00a1: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_00a6: pop + IL_00a7: endfinally + } + IL_00a8: ret + } + """); } [Fact] @@ -3816,239 +3847,248 @@ static void Main() CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (27,26): error CS9328: Method 'TestCase.Run(T)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[0, 0] += await GetVal(4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(4)").WithArguments("TestCase.Run(T)").WithLocation(27, 26), - // (32,26): error CS9328: Method 'TestCase.Run(T)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[1, 1] += await (GetVal(arr[0, 0])); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await (GetVal(arr[0, 0]))").WithArguments("TestCase.Run(T)").WithLocation(32, 26), - // (37,26): error CS9328: Method 'TestCase.Run(T)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[1, 1] += await (GetVal(arr[0, await GetVal(0)])); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await (GetVal(arr[0, await GetVal(0)]))").WithArguments("TestCase.Run(T)").WithLocation(37, 26) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // [Run]: Return value missing on the stack. { Offset = 0x178 } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run(T)", """ - // { - // // Code size 377 (0x179) - // .maxstack 5 - // .locals init (int V_0, //tests - // int[,] V_1, //arr - // int V_2, - // int V_3, - // int[,] V_4, - // int V_5) - // IL_0000: ldc.i4.0 - // IL_0001: stloc.0 - // .try - // { - // IL_0002: ldarg.0 - // IL_0003: ldc.i4.4 - // IL_0004: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0009: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_000e: ldarg.0 - // IL_000f: ldc.i4.4 - // IL_0010: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0015: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_001a: stloc.2 - // IL_001b: ldloc.2 - // IL_001c: newobj "int[*,*]..ctor" - // IL_0021: stloc.1 - // IL_0022: ldloc.0 - // IL_0023: ldc.i4.1 - // IL_0024: add - // IL_0025: stloc.0 - // IL_0026: ldloc.1 - // IL_0027: ldarg.0 - // IL_0028: ldc.i4.4 - // IL_0029: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_002e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0033: stloc.2 - // IL_0034: ldc.i4.0 - // IL_0035: ldc.i4.0 - // IL_0036: ldloc.2 - // IL_0037: call "int[*,*].Set" - // IL_003c: ldloc.1 - // IL_003d: ldarg.0 - // IL_003e: ldc.i4.0 - // IL_003f: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0044: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0049: stloc.2 - // IL_004a: ldc.i4.0 - // IL_004b: ldloc.2 - // IL_004c: call "int[*,*].Get" - // IL_0051: ldc.i4.4 - // IL_0052: bne.un.s IL_0060 - // IL_0054: ldsfld "int Driver.Count" - // IL_0059: ldc.i4.1 - // IL_005a: add - // IL_005b: stsfld "int Driver.Count" - // IL_0060: ldloc.0 - // IL_0061: ldc.i4.1 - // IL_0062: add - // IL_0063: stloc.0 - // IL_0064: ldloc.1 - // IL_0065: ldc.i4.0 - // IL_0066: ldc.i4.0 - // IL_0067: call "int[*,*].Address" - // IL_006c: dup - // IL_006d: ldind.i4 - // IL_006e: stloc.2 - // IL_006f: ldarg.0 - // IL_0070: ldc.i4.4 - // IL_0071: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0076: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_007b: stloc.3 - // IL_007c: ldloc.2 - // IL_007d: ldloc.3 - // IL_007e: add - // IL_007f: stind.i4 - // IL_0080: ldloc.1 - // IL_0081: ldc.i4.0 - // IL_0082: ldc.i4.0 - // IL_0083: call "int[*,*].Get" - // IL_0088: ldc.i4.8 - // IL_0089: bne.un.s IL_0097 - // IL_008b: ldsfld "int Driver.Count" - // IL_0090: ldc.i4.1 - // IL_0091: add - // IL_0092: stsfld "int Driver.Count" - // IL_0097: ldloc.0 - // IL_0098: ldc.i4.1 - // IL_0099: add - // IL_009a: stloc.0 - // IL_009b: ldloc.1 - // IL_009c: ldc.i4.1 - // IL_009d: ldc.i4.1 - // IL_009e: call "int[*,*].Address" - // IL_00a3: dup - // IL_00a4: ldind.i4 - // IL_00a5: stloc.3 - // IL_00a6: ldarg.0 - // IL_00a7: ldloc.1 - // IL_00a8: ldc.i4.0 - // IL_00a9: ldc.i4.0 - // IL_00aa: call "int[*,*].Get" - // IL_00af: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_00b4: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_00b9: stloc.2 - // IL_00ba: ldloc.3 - // IL_00bb: ldloc.2 - // IL_00bc: add - // IL_00bd: stind.i4 - // IL_00be: ldloc.1 - // IL_00bf: ldc.i4.1 - // IL_00c0: ldc.i4.1 - // IL_00c1: call "int[*,*].Get" - // IL_00c6: ldc.i4.8 - // IL_00c7: bne.un.s IL_00d5 - // IL_00c9: ldsfld "int Driver.Count" - // IL_00ce: ldc.i4.1 - // IL_00cf: add - // IL_00d0: stsfld "int Driver.Count" - // IL_00d5: ldloc.0 - // IL_00d6: ldc.i4.1 - // IL_00d7: add - // IL_00d8: stloc.0 - // IL_00d9: ldloc.1 - // IL_00da: ldc.i4.1 - // IL_00db: ldc.i4.1 - // IL_00dc: call "int[*,*].Address" - // IL_00e1: dup - // IL_00e2: ldind.i4 - // IL_00e3: stloc.2 - // IL_00e4: ldloc.1 - // IL_00e5: stloc.s V_4 - // IL_00e7: ldarg.0 - // IL_00e8: ldc.i4.0 - // IL_00e9: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_00ee: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_00f3: stloc.s V_5 - // IL_00f5: ldarg.0 - // IL_00f6: ldloc.s V_4 - // IL_00f8: ldc.i4.0 - // IL_00f9: ldloc.s V_5 - // IL_00fb: call "int[*,*].Get" - // IL_0100: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0105: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_010a: stloc.3 - // IL_010b: ldloc.2 - // IL_010c: ldloc.3 - // IL_010d: add - // IL_010e: stind.i4 - // IL_010f: ldloc.1 - // IL_0110: ldc.i4.1 - // IL_0111: ldc.i4.1 - // IL_0112: call "int[*,*].Get" - // IL_0117: ldc.i4.s 16 - // IL_0119: bne.un.s IL_0127 - // IL_011b: ldsfld "int Driver.Count" - // IL_0120: ldc.i4.1 - // IL_0121: add - // IL_0122: stsfld "int Driver.Count" - // IL_0127: ldloc.0 - // IL_0128: ldc.i4.1 - // IL_0129: add - // IL_012a: stloc.0 - // IL_012b: ldloc.1 - // IL_012c: ldarg.0 - // IL_012d: ldc.i4.2 - // IL_012e: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0133: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0138: stloc.3 - // IL_0139: ldc.i4.2 - // IL_013a: ldloc.3 - // IL_013b: call "int[*,*].Address" - // IL_0140: dup - // IL_0141: ldind.i4 - // IL_0142: stloc.2 - // IL_0143: ldloc.2 - // IL_0144: ldc.i4.1 - // IL_0145: add - // IL_0146: stind.i4 - // IL_0147: ldloc.1 - // IL_0148: ldc.i4.2 - // IL_0149: ldc.i4.2 - // IL_014a: call "int[*,*].Get" - // IL_014f: ldc.i4.1 - // IL_0150: bne.un.s IL_015e - // IL_0152: ldsfld "int Driver.Count" - // IL_0157: ldc.i4.1 - // IL_0158: add - // IL_0159: stsfld "int Driver.Count" - // IL_015e: leave.s IL_0178 - // } - // finally - // { - // IL_0160: ldsfld "int Driver.Count" - // IL_0165: ldloc.0 - // IL_0166: sub - // IL_0167: stsfld "int Driver.Result" - // IL_016c: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" - // IL_0171: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_0176: pop - // IL_0177: endfinally - // } - // IL_0178: ret - // } - // """); - } + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x1a5 } + """ + }); - [Fact] - public void SpillArray04() - { - var source = @" -using System.Threading; -using System.Threading.Tasks; + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run(T)", """ + { + // Code size 422 (0x1a6) + .maxstack 5 + .locals init (int V_0, //tests + int[,] V_1, //arr + int V_2, + int V_3, + int[,] V_4, + int V_5) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldarg.0 + IL_0003: ldc.i4.4 + IL_0004: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0009: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000e: ldarg.0 + IL_000f: ldc.i4.4 + IL_0010: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0015: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001a: stloc.2 + IL_001b: ldloc.2 + IL_001c: newobj "int[*,*]..ctor" + IL_0021: stloc.1 + IL_0022: ldloc.0 + IL_0023: ldc.i4.1 + IL_0024: add + IL_0025: stloc.0 + IL_0026: ldloc.1 + IL_0027: ldarg.0 + IL_0028: ldc.i4.4 + IL_0029: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_002e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0033: stloc.2 + IL_0034: ldc.i4.0 + IL_0035: ldc.i4.0 + IL_0036: ldloc.2 + IL_0037: call "int[*,*].Set" + IL_003c: ldloc.1 + IL_003d: ldarg.0 + IL_003e: ldc.i4.0 + IL_003f: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0044: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0049: stloc.2 + IL_004a: ldc.i4.0 + IL_004b: ldloc.2 + IL_004c: call "int[*,*].Get" + IL_0051: ldc.i4.4 + IL_0052: bne.un.s IL_0060 + IL_0054: ldsfld "int Driver.Count" + IL_0059: ldc.i4.1 + IL_005a: add + IL_005b: stsfld "int Driver.Count" + IL_0060: ldloc.0 + IL_0061: ldc.i4.1 + IL_0062: add + IL_0063: stloc.0 + IL_0064: ldloc.1 + IL_0065: dup + IL_0066: ldc.i4.0 + IL_0067: ldc.i4.0 + IL_0068: call "int[*,*].Get" + IL_006d: pop + IL_006e: dup + IL_006f: ldc.i4.0 + IL_0070: ldc.i4.0 + IL_0071: call "int[*,*].Get" + IL_0076: stloc.2 + IL_0077: ldarg.0 + IL_0078: ldc.i4.4 + IL_0079: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_007e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0083: stloc.3 + IL_0084: ldc.i4.0 + IL_0085: ldc.i4.0 + IL_0086: call "int[*,*].Address" + IL_008b: ldloc.2 + IL_008c: ldloc.3 + IL_008d: add + IL_008e: stind.i4 + IL_008f: ldloc.1 + IL_0090: ldc.i4.0 + IL_0091: ldc.i4.0 + IL_0092: call "int[*,*].Get" + IL_0097: ldc.i4.8 + IL_0098: bne.un.s IL_00a6 + IL_009a: ldsfld "int Driver.Count" + IL_009f: ldc.i4.1 + IL_00a0: add + IL_00a1: stsfld "int Driver.Count" + IL_00a6: ldloc.0 + IL_00a7: ldc.i4.1 + IL_00a8: add + IL_00a9: stloc.0 + IL_00aa: ldloc.1 + IL_00ab: dup + IL_00ac: ldc.i4.1 + IL_00ad: ldc.i4.1 + IL_00ae: call "int[*,*].Get" + IL_00b3: pop + IL_00b4: dup + IL_00b5: ldc.i4.1 + IL_00b6: ldc.i4.1 + IL_00b7: call "int[*,*].Get" + IL_00bc: stloc.3 + IL_00bd: ldarg.0 + IL_00be: ldloc.1 + IL_00bf: ldc.i4.0 + IL_00c0: ldc.i4.0 + IL_00c1: call "int[*,*].Get" + IL_00c6: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_00cb: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_00d0: stloc.2 + IL_00d1: ldc.i4.1 + IL_00d2: ldc.i4.1 + IL_00d3: call "int[*,*].Address" + IL_00d8: ldloc.3 + IL_00d9: ldloc.2 + IL_00da: add + IL_00db: stind.i4 + IL_00dc: ldloc.1 + IL_00dd: ldc.i4.1 + IL_00de: ldc.i4.1 + IL_00df: call "int[*,*].Get" + IL_00e4: ldc.i4.8 + IL_00e5: bne.un.s IL_00f3 + IL_00e7: ldsfld "int Driver.Count" + IL_00ec: ldc.i4.1 + IL_00ed: add + IL_00ee: stsfld "int Driver.Count" + IL_00f3: ldloc.0 + IL_00f4: ldc.i4.1 + IL_00f5: add + IL_00f6: stloc.0 + IL_00f7: ldloc.1 + IL_00f8: dup + IL_00f9: ldc.i4.1 + IL_00fa: ldc.i4.1 + IL_00fb: call "int[*,*].Get" + IL_0100: pop + IL_0101: dup + IL_0102: ldc.i4.1 + IL_0103: ldc.i4.1 + IL_0104: call "int[*,*].Get" + IL_0109: stloc.2 + IL_010a: ldloc.1 + IL_010b: stloc.s V_4 + IL_010d: ldarg.0 + IL_010e: ldc.i4.0 + IL_010f: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0114: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0119: stloc.s V_5 + IL_011b: ldarg.0 + IL_011c: ldloc.s V_4 + IL_011e: ldc.i4.0 + IL_011f: ldloc.s V_5 + IL_0121: call "int[*,*].Get" + IL_0126: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_012b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0130: stloc.3 + IL_0131: ldc.i4.1 + IL_0132: ldc.i4.1 + IL_0133: call "int[*,*].Address" + IL_0138: ldloc.2 + IL_0139: ldloc.3 + IL_013a: add + IL_013b: stind.i4 + IL_013c: ldloc.1 + IL_013d: ldc.i4.1 + IL_013e: ldc.i4.1 + IL_013f: call "int[*,*].Get" + IL_0144: ldc.i4.s 16 + IL_0146: bne.un.s IL_0154 + IL_0148: ldsfld "int Driver.Count" + IL_014d: ldc.i4.1 + IL_014e: add + IL_014f: stsfld "int Driver.Count" + IL_0154: ldloc.0 + IL_0155: ldc.i4.1 + IL_0156: add + IL_0157: stloc.0 + IL_0158: ldloc.1 + IL_0159: ldarg.0 + IL_015a: ldc.i4.2 + IL_015b: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0160: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0165: stloc.3 + IL_0166: ldc.i4.2 + IL_0167: ldloc.3 + IL_0168: call "int[*,*].Address" + IL_016d: dup + IL_016e: ldind.i4 + IL_016f: stloc.2 + IL_0170: ldloc.2 + IL_0171: ldc.i4.1 + IL_0172: add + IL_0173: stind.i4 + IL_0174: ldloc.1 + IL_0175: ldc.i4.2 + IL_0176: ldc.i4.2 + IL_0177: call "int[*,*].Get" + IL_017c: ldc.i4.1 + IL_017d: bne.un.s IL_018b + IL_017f: ldsfld "int Driver.Count" + IL_0184: ldc.i4.1 + IL_0185: add + IL_0186: stsfld "int Driver.Count" + IL_018b: leave.s IL_01a5 + } + finally + { + IL_018d: ldsfld "int Driver.Count" + IL_0192: ldloc.0 + IL_0193: sub + IL_0194: stsfld "int Driver.Result" + IL_0199: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_019e: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_01a3: pop + IL_01a4: endfinally + } + IL_01a5: ret + } + """); + } + + [Fact] + public void SpillArray04() + { + var source = @" +using System.Threading; +using System.Threading.Tasks; struct MyStruct { @@ -4159,51 +4199,49 @@ static void Main() CompileAndVerify(source, ""); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (23,23): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // public async Task Run() - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "Run").WithArguments("TestCase.Run()").WithLocation(23, 23) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("", isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Run]: Return value missing on the stack. { Offset = 0x2b } - // [Goo]: Unexpected type on the stack. { Offset = 0xc, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run()", """ - // { - // // Code size 44 (0x2c) - // .maxstack 2 - // .locals init (MyStruct V_0, //ms - // int V_1) - // .try - // { - // IL_0000: ldloca.s V_0 - // IL_0002: initobj "MyStruct" - // IL_0008: ldarg.0 - // IL_0009: call "System.Threading.Tasks.Task TestCase.Goo()" - // IL_000e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0013: stloc.1 - // IL_0014: ldloca.s V_0 - // IL_0016: ldloc.1 - // IL_0017: call "int MyStruct.this[int].get" - // IL_001c: pop - // IL_001d: leave.s IL_002b - // } - // finally - // { - // IL_001f: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" - // IL_0024: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_0029: pop - // IL_002a: endfinally - // } - // IL_002b: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Run]: Return value missing on the stack. { Offset = 0x33 } + [Goo]: Unexpected type on the stack. { Offset = 0xc, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run()", """ + { + // Code size 52 (0x34) + .maxstack 2 + .locals init (TestCase V_0, + MyStruct V_1, //ms + int V_2) + IL_0000: ldarg.0 + IL_0001: ldobj "TestCase" + IL_0006: stloc.0 + .try + { + IL_0007: ldloca.s V_1 + IL_0009: initobj "MyStruct" + IL_000f: ldloca.s V_0 + IL_0011: call "System.Threading.Tasks.Task TestCase.Goo()" + IL_0016: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001b: stloc.2 + IL_001c: ldloca.s V_1 + IL_001e: ldloc.2 + IL_001f: call "int MyStruct.this[int].get" + IL_0024: pop + IL_0025: leave.s IL_0033 + } + finally + { + IL_0027: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_002c: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_0031: pop + IL_0032: endfinally + } + IL_0033: ret + } + """); } [Fact] @@ -4599,55 +4637,54 @@ static void Main() CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (12,19): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[0] += await Task.Factory.StartNew(() => 42); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 42)").WithArguments("Driver.Run()").WithLocation(12, 19) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Run]: Return value missing on the stack. { Offset = 0x4c } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("Driver.Run()", """ - // { - // // Code size 77 (0x4d) - // .maxstack 4 - // .locals init (int V_0, - // int V_1) - // IL_0000: ldc.i4.1 - // IL_0001: newarr "int" - // IL_0006: stsfld "int[] Driver.arr" - // IL_000b: ldsfld "int[] Driver.arr" - // IL_0010: ldc.i4.0 - // IL_0011: ldelema "int" - // IL_0016: dup - // IL_0017: ldind.i4 - // IL_0018: stloc.0 - // IL_0019: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_001e: ldsfld "System.Func Driver.<>c.<>9__1_0" - // IL_0023: dup - // IL_0024: brtrue.s IL_003d - // IL_0026: pop - // IL_0027: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_002c: ldftn "int Driver.<>c.b__1_0()" - // IL_0032: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_0037: dup - // IL_0038: stsfld "System.Func Driver.<>c.<>9__1_0" - // IL_003d: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_0042: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0047: stloc.1 - // IL_0048: ldloc.0 - // IL_0049: ldloc.1 - // IL_004a: add - // IL_004b: stind.i4 - // IL_004c: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Run]: Return value missing on the stack. { Offset = 0x51 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Driver.Run()", """ + { + // Code size 82 (0x52) + .maxstack 4 + .locals init (int V_0, + int V_1) + IL_0000: ldc.i4.1 + IL_0001: newarr "int" + IL_0006: stsfld "int[] Driver.arr" + IL_000b: ldsfld "int[] Driver.arr" + IL_0010: dup + IL_0011: ldc.i4.0 + IL_0012: ldelem.i4 + IL_0013: pop + IL_0014: dup + IL_0015: ldc.i4.0 + IL_0016: ldelem.i4 + IL_0017: stloc.0 + IL_0018: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_001d: ldsfld "System.Func Driver.<>c.<>9__1_0" + IL_0022: dup + IL_0023: brtrue.s IL_003c + IL_0025: pop + IL_0026: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_002b: ldftn "int Driver.<>c.b__1_0()" + IL_0031: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0036: dup + IL_0037: stsfld "System.Func Driver.<>c.<>9__1_0" + IL_003c: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0041: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0046: stloc.1 + IL_0047: ldc.i4.0 + IL_0048: ldelema "int" + IL_004d: ldloc.0 + IL_004e: ldloc.1 + IL_004f: add + IL_0050: stind.i4 + IL_0051: ret + } + """); } [Fact] @@ -4679,69 +4716,71 @@ static void Main() CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (12,13): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[await Task.Factory.StartNew(() => 0)] += await Task.Factory.StartNew(() => 42); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(12, 13) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Run]: Return value missing on the stack. { Offset = 0x7b } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("Driver.Run()", """ - // { - // // Code size 124 (0x7c) - // .maxstack 4 - // .locals init (int V_0, - // int V_1, - // int V_2) - // IL_0000: ldc.i4.1 - // IL_0001: newarr "int" - // IL_0006: stsfld "int[] Driver.arr" - // IL_000b: ldsfld "int[] Driver.arr" - // IL_0010: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_0015: ldsfld "System.Func Driver.<>c.<>9__1_0" - // IL_001a: dup - // IL_001b: brtrue.s IL_0034 - // IL_001d: pop - // IL_001e: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_0023: ldftn "int Driver.<>c.b__1_0()" - // IL_0029: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_002e: dup - // IL_002f: stsfld "System.Func Driver.<>c.<>9__1_0" - // IL_0034: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_0039: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_003e: stloc.0 - // IL_003f: ldloc.0 - // IL_0040: ldelema "int" - // IL_0045: dup - // IL_0046: ldind.i4 - // IL_0047: stloc.1 - // IL_0048: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_004d: ldsfld "System.Func Driver.<>c.<>9__1_1" - // IL_0052: dup - // IL_0053: brtrue.s IL_006c - // IL_0055: pop - // IL_0056: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_005b: ldftn "int Driver.<>c.b__1_1()" - // IL_0061: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_0066: dup - // IL_0067: stsfld "System.Func Driver.<>c.<>9__1_1" - // IL_006c: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_0071: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0076: stloc.2 - // IL_0077: ldloc.1 - // IL_0078: ldloc.2 - // IL_0079: add - // IL_007a: stind.i4 - // IL_007b: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Run]: Return value missing on the stack. { Offset = 0x82 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Driver.Run()", """ + { + // Code size 131 (0x83) + .maxstack 4 + .locals init (int V_0, + int V_1, + int V_2, + int V_3) + IL_0000: ldc.i4.1 + IL_0001: newarr "int" + IL_0006: stsfld "int[] Driver.arr" + IL_000b: ldsfld "int[] Driver.arr" + IL_0010: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_0015: ldsfld "System.Func Driver.<>c.<>9__1_0" + IL_001a: dup + IL_001b: brtrue.s IL_0034 + IL_001d: pop + IL_001e: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_0023: ldftn "int Driver.<>c.b__1_0()" + IL_0029: newobj "System.Func..ctor(object, System.IntPtr)" + IL_002e: dup + IL_002f: stsfld "System.Func Driver.<>c.<>9__1_0" + IL_0034: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0039: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003e: stloc.1 + IL_003f: ldloc.1 + IL_0040: stloc.0 + IL_0041: dup + IL_0042: ldloc.0 + IL_0043: ldelem.i4 + IL_0044: pop + IL_0045: dup + IL_0046: ldloc.0 + IL_0047: ldelem.i4 + IL_0048: stloc.2 + IL_0049: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_004e: ldsfld "System.Func Driver.<>c.<>9__1_1" + IL_0053: dup + IL_0054: brtrue.s IL_006d + IL_0056: pop + IL_0057: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_005c: ldftn "int Driver.<>c.b__1_1()" + IL_0062: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0067: dup + IL_0068: stsfld "System.Func Driver.<>c.<>9__1_1" + IL_006d: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0072: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0077: stloc.3 + IL_0078: ldloc.0 + IL_0079: ldelema "int" + IL_007e: ldloc.2 + IL_007f: ldloc.3 + IL_0080: add + IL_0081: stind.i4 + IL_0082: ret + } + """); } [Fact] @@ -4783,90 +4822,98 @@ static void Main() CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (20,13): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(20, 13), - // (20,13): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(20, 13) - ); - // https://github.com/dotnet/roslyn/issues/79763 - support struct lifting - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Run]: Unexpected type on the stack. { Offset = 0xbb, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("Driver.Run()", """ - // { - // // Code size 188 (0xbc) - // .maxstack 5 - // .locals init (int V_0, - // int V_1, - // int V_2) - // IL_0000: ldc.i4.1 - // IL_0001: newarr "S2" - // IL_0006: dup - // IL_0007: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_000c: ldsfld "System.Func Driver.<>c.<>9__0_0" - // IL_0011: dup - // IL_0012: brtrue.s IL_002b - // IL_0014: pop - // IL_0015: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_001a: ldftn "int Driver.<>c.b__0_0()" - // IL_0020: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_0025: dup - // IL_0026: stsfld "System.Func Driver.<>c.<>9__0_0" - // IL_002b: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_0030: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0035: stloc.0 - // IL_0036: ldloc.0 - // IL_0037: ldelema "S2" - // IL_003c: ldflda "S1 S2.s1" - // IL_0041: ldflda "int S1.x" - // IL_0046: dup - // IL_0047: ldind.i4 - // IL_0048: stloc.1 - // IL_0049: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_004e: ldsfld "System.Func Driver.<>c.<>9__0_1" - // IL_0053: dup - // IL_0054: brtrue.s IL_006d - // IL_0056: pop - // IL_0057: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_005c: ldftn "int Driver.<>c.b__0_1()" - // IL_0062: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_0067: dup - // IL_0068: stsfld "System.Func Driver.<>c.<>9__0_1" - // IL_006d: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_0072: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0077: stloc.2 - // IL_0078: ldloc.1 - // IL_0079: ldloc.2 - // IL_007a: add - // IL_007b: stind.i4 - // IL_007c: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_0081: ldsfld "System.Func Driver.<>c.<>9__0_2" - // IL_0086: dup - // IL_0087: brtrue.s IL_00a0 - // IL_0089: pop - // IL_008a: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_008f: ldftn "int Driver.<>c.b__0_2()" - // IL_0095: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_009a: dup - // IL_009b: stsfld "System.Func Driver.<>c.<>9__0_2" - // IL_00a0: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_00a5: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_00aa: stloc.2 - // IL_00ab: ldloc.2 - // IL_00ac: ldelema "S2" - // IL_00b1: ldflda "S1 S2.s1" - // IL_00b6: ldfld "int S1.x" - // IL_00bb: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Run]: Unexpected type on the stack. { Offset = 0xe2, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Driver.Run()", """ + { + // Code size 227 (0xe3) + .maxstack 5 + .locals init (int V_0, + int V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldc.i4.1 + IL_0001: newarr "S2" + IL_0006: dup + IL_0007: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_000c: ldsfld "System.Func Driver.<>c.<>9__0_0" + IL_0011: dup + IL_0012: brtrue.s IL_002b + IL_0014: pop + IL_0015: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_001a: ldftn "int Driver.<>c.b__0_0()" + IL_0020: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0025: dup + IL_0026: stsfld "System.Func Driver.<>c.<>9__0_0" + IL_002b: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0030: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0035: stloc.2 + IL_0036: ldloc.2 + IL_0037: stloc.0 + IL_0038: dup + IL_0039: ldloc.0 + IL_003a: ldelema "S2" + IL_003f: pop + IL_0040: ldloc.0 + IL_0041: stloc.1 + IL_0042: dup + IL_0043: ldloc.1 + IL_0044: ldelema "S2" + IL_0049: pop + IL_004a: dup + IL_004b: ldloc.1 + IL_004c: ldelema "S2" + IL_0051: ldflda "S1 S2.s1" + IL_0056: ldfld "int S1.x" + IL_005b: stloc.3 + IL_005c: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_0061: ldsfld "System.Func Driver.<>c.<>9__0_1" + IL_0066: dup + IL_0067: brtrue.s IL_0080 + IL_0069: pop + IL_006a: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_006f: ldftn "int Driver.<>c.b__0_1()" + IL_0075: newobj "System.Func..ctor(object, System.IntPtr)" + IL_007a: dup + IL_007b: stsfld "System.Func Driver.<>c.<>9__0_1" + IL_0080: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0085: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_008a: stloc.s V_4 + IL_008c: ldloc.1 + IL_008d: ldelema "S2" + IL_0092: ldflda "S1 S2.s1" + IL_0097: ldflda "int S1.x" + IL_009c: ldloc.3 + IL_009d: ldloc.s V_4 + IL_009f: add + IL_00a0: stind.i4 + IL_00a1: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_00a6: ldsfld "System.Func Driver.<>c.<>9__0_2" + IL_00ab: dup + IL_00ac: brtrue.s IL_00c5 + IL_00ae: pop + IL_00af: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_00b4: ldftn "int Driver.<>c.b__0_2()" + IL_00ba: newobj "System.Func..ctor(object, System.IntPtr)" + IL_00bf: dup + IL_00c0: stsfld "System.Func Driver.<>c.<>9__0_2" + IL_00c5: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_00ca: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_00cf: stloc.s V_4 + IL_00d1: ldloc.s V_4 + IL_00d3: ldelema "S2" + IL_00d8: ldflda "S1 S2.s1" + IL_00dd: ldfld "int S1.x" + IL_00e2: ret + } + """); } [Fact] @@ -4908,132 +4955,288 @@ static void Main() CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (20,64): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[await Task.Factory.StartNew(() => 0)].s1.x += (arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42)); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(20, 64), - // (20,64): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[await Task.Factory.StartNew(() => 0)].s1.x += (arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42)); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(20, 64), - // (20,13): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[await Task.Factory.StartNew(() => 0)].s1.x += (arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42)); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(20, 13), - // (20,13): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[await Task.Factory.StartNew(() => 0)].s1.x += (arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42)); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(20, 13) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Run]: Unexpected type on the stack. { Offset = 0x113, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("Driver.Run()", """ - // { - // // Code size 276 (0x114) - // .maxstack 6 - // .locals init (int V_0, - // int& V_1, - // int V_2, - // int V_3, - // int& V_4, - // int V_5, - // int V_6, - // int V_7) - // IL_0000: ldc.i4.1 - // IL_0001: newarr "S2" - // IL_0006: dup - // IL_0007: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_000c: ldsfld "System.Func Driver.<>c.<>9__0_0" - // IL_0011: dup - // IL_0012: brtrue.s IL_002b - // IL_0014: pop - // IL_0015: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_001a: ldftn "int Driver.<>c.b__0_0()" - // IL_0020: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_0025: dup - // IL_0026: stsfld "System.Func Driver.<>c.<>9__0_0" - // IL_002b: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_0030: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0035: stloc.0 - // IL_0036: ldloc.0 - // IL_0037: ldelema "S2" - // IL_003c: ldflda "S1 S2.s1" - // IL_0041: ldflda "int S1.x" - // IL_0046: stloc.1 - // IL_0047: ldloc.1 - // IL_0048: ldind.i4 - // IL_0049: stloc.2 - // IL_004a: dup - // IL_004b: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_0050: ldsfld "System.Func Driver.<>c.<>9__0_1" - // IL_0055: dup - // IL_0056: brtrue.s IL_006f - // IL_0058: pop - // IL_0059: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_005e: ldftn "int Driver.<>c.b__0_1()" - // IL_0064: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_0069: dup - // IL_006a: stsfld "System.Func Driver.<>c.<>9__0_1" - // IL_006f: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_0074: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0079: stloc.3 - // IL_007a: ldloc.3 - // IL_007b: ldelema "S2" - // IL_0080: ldflda "S1 S2.s1" - // IL_0085: ldflda "int S1.x" - // IL_008a: stloc.s V_4 - // IL_008c: ldloc.s V_4 - // IL_008e: ldind.i4 - // IL_008f: stloc.s V_5 - // IL_0091: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_0096: ldsfld "System.Func Driver.<>c.<>9__0_2" - // IL_009b: dup - // IL_009c: brtrue.s IL_00b5 - // IL_009e: pop - // IL_009f: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_00a4: ldftn "int Driver.<>c.b__0_2()" - // IL_00aa: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_00af: dup - // IL_00b0: stsfld "System.Func Driver.<>c.<>9__0_2" - // IL_00b5: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_00ba: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_00bf: stloc.s V_6 - // IL_00c1: ldloc.1 - // IL_00c2: ldloc.2 - // IL_00c3: ldloc.s V_4 - // IL_00c5: ldloc.s V_5 - // IL_00c7: ldloc.s V_6 - // IL_00c9: add - // IL_00ca: dup - // IL_00cb: stloc.s V_7 - // IL_00cd: stind.i4 - // IL_00ce: ldloc.s V_7 - // IL_00d0: add - // IL_00d1: stind.i4 - // IL_00d2: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_00d7: ldsfld "System.Func Driver.<>c.<>9__0_3" - // IL_00dc: dup - // IL_00dd: brtrue.s IL_00f6 - // IL_00df: pop - // IL_00e0: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_00e5: ldftn "int Driver.<>c.b__0_3()" - // IL_00eb: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_00f0: dup - // IL_00f1: stsfld "System.Func Driver.<>c.<>9__0_3" - // IL_00f6: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_00fb: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0100: stloc.s V_6 - // IL_0102: ldloc.s V_6 - // IL_0104: ldelema "S2" - // IL_0109: ldflda "S1 S2.s1" - // IL_010e: ldfld "int S1.x" - // IL_0113: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Run]: Unexpected type on the stack. { Offset = 0x168, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Driver.Run()", """ + { + // Code size 361 (0x169) + .maxstack 6 + .locals init (int V_0, + S2[] V_1, + int V_2, + int V_3, + S2[] V_4, + int V_5, + int V_6, + int V_7, + int V_8, + int& V_9, + int V_10, + int V_11, + int V_12) + IL_0000: ldc.i4.1 + IL_0001: newarr "S2" + IL_0006: dup + IL_0007: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_000c: ldsfld "System.Func Driver.<>c.<>9__0_0" + IL_0011: dup + IL_0012: brtrue.s IL_002b + IL_0014: pop + IL_0015: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_001a: ldftn "int Driver.<>c.b__0_0()" + IL_0020: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0025: dup + IL_0026: stsfld "System.Func Driver.<>c.<>9__0_0" + IL_002b: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0030: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0035: stloc.s V_6 + IL_0037: ldloc.s V_6 + IL_0039: stloc.0 + IL_003a: dup + IL_003b: ldloc.0 + IL_003c: ldelema "S2" + IL_0041: pop + IL_0042: stloc.1 + IL_0043: ldloc.0 + IL_0044: stloc.2 + IL_0045: ldloc.1 + IL_0046: ldloc.2 + IL_0047: ldelema "S2" + IL_004c: pop + IL_004d: ldloc.1 + IL_004e: ldloc.2 + IL_004f: ldelema "S2" + IL_0054: ldflda "S1 S2.s1" + IL_0059: ldfld "int S1.x" + IL_005e: stloc.s V_7 + IL_0060: dup + IL_0061: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_0066: ldsfld "System.Func Driver.<>c.<>9__0_1" + IL_006b: dup + IL_006c: brtrue.s IL_0085 + IL_006e: pop + IL_006f: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_0074: ldftn "int Driver.<>c.b__0_1()" + IL_007a: newobj "System.Func..ctor(object, System.IntPtr)" + IL_007f: dup + IL_0080: stsfld "System.Func Driver.<>c.<>9__0_1" + IL_0085: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_008a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_008f: stloc.s V_8 + IL_0091: ldloc.s V_8 + IL_0093: stloc.3 + IL_0094: dup + IL_0095: ldloc.3 + IL_0096: ldelema "S2" + IL_009b: pop + IL_009c: stloc.s V_4 + IL_009e: ldloc.3 + IL_009f: stloc.s V_5 + IL_00a1: ldloc.s V_4 + IL_00a3: ldloc.s V_5 + IL_00a5: ldelema "S2" + IL_00aa: pop + IL_00ab: ldloc.s V_4 + IL_00ad: ldloc.s V_5 + IL_00af: ldelema "S2" + IL_00b4: ldflda "S1 S2.s1" + IL_00b9: ldfld "int S1.x" + IL_00be: stloc.s V_10 + IL_00c0: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_00c5: ldsfld "System.Func Driver.<>c.<>9__0_2" + IL_00ca: dup + IL_00cb: brtrue.s IL_00e4 + IL_00cd: pop + IL_00ce: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_00d3: ldftn "int Driver.<>c.b__0_2()" + IL_00d9: newobj "System.Func..ctor(object, System.IntPtr)" + IL_00de: dup + IL_00df: stsfld "System.Func Driver.<>c.<>9__0_2" + IL_00e4: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_00e9: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_00ee: stloc.s V_11 + IL_00f0: ldloc.1 + IL_00f1: ldloc.2 + IL_00f2: ldelema "S2" + IL_00f7: ldflda "S1 S2.s1" + IL_00fc: ldflda "int S1.x" + IL_0101: ldloc.s V_7 + IL_0103: ldloc.s V_4 + IL_0105: ldloc.s V_5 + IL_0107: ldelema "S2" + IL_010c: ldflda "S1 S2.s1" + IL_0111: ldflda "int S1.x" + IL_0116: stloc.s V_9 + IL_0118: ldloc.s V_9 + IL_011a: ldloc.s V_10 + IL_011c: ldloc.s V_11 + IL_011e: add + IL_011f: dup + IL_0120: stloc.s V_12 + IL_0122: stind.i4 + IL_0123: ldloc.s V_12 + IL_0125: add + IL_0126: stind.i4 + IL_0127: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_012c: ldsfld "System.Func Driver.<>c.<>9__0_3" + IL_0131: dup + IL_0132: brtrue.s IL_014b + IL_0134: pop + IL_0135: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_013a: ldftn "int Driver.<>c.b__0_3()" + IL_0140: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0145: dup + IL_0146: stsfld "System.Func Driver.<>c.<>9__0_3" + IL_014b: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0150: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0155: stloc.s V_11 + IL_0157: ldloc.s V_11 + IL_0159: ldelema "S2" + IL_015e: ldflda "S1 S2.s1" + IL_0163: ldfld "int S1.x" + IL_0168: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79415")] + public void CompoundAssignment() + { + var source = """ + using System; + using System.Threading.Tasks; + + interface I1 + { + int this[int i] { get; set; } + } + + struct S1 : I1 + { + public int F1; + + public int this[int i] + { + get + { + Console.Write(" g" + i); + return 0; + } + set + { + Console.Write(" s" + i + value); + } + } + } + + class Program + { + static async Task Main() + { + await Test3(); + } + + static T GetT() where T : I1 + { + Console.Write(" t"); + return (T)(object)new S1 { F1 = 123 }; + } + + static async Task Test3() where T : I1 + { + GetT()[Index()] += await Get1Async(); + } + + static async Task Get1Async() + { + Console.Write(" v"); + await Task.Yield(); + return 1; + } + + static int Index() + { + Console.Write(" i"); + return 2; + } + } + """; + var expectedOutput = "t i g2 v s21"; + CompileAndVerify(source, expectedOutput: expectedOutput).VerifyDiagnostics(); + + var comp = CreateRuntimeAsyncCompilation(source); + + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xa } + [Test3]: Return value missing on the stack. { Offset = 0x7a } + [Get1Async]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 123 (0x7b) + .maxstack 4 + .locals init (T V_0, + T V_1, + int V_2, + int V_3, + int V_4, + int V_5, + T V_6) + IL_0000: call "T Program.GetT()" + IL_0005: stloc.0 + IL_0006: ldloca.s V_6 + IL_0008: initobj "T" + IL_000e: ldloc.s V_6 + IL_0010: box "T" + IL_0015: brtrue.s IL_0019 + IL_0017: ldloc.0 + IL_0018: stloc.1 + IL_0019: call "int Program.Index()" + IL_001e: stloc.2 + IL_001f: ldloc.2 + IL_0020: stloc.3 + IL_0021: ldloca.s V_6 + IL_0023: initobj "T" + IL_0029: ldloc.s V_6 + IL_002b: box "T" + IL_0030: brtrue.s IL_0036 + IL_0032: ldloca.s V_1 + IL_0034: br.s IL_0038 + IL_0036: ldloca.s V_0 + IL_0038: ldloc.2 + IL_0039: constrained. "T" + IL_003f: callvirt "int I1.this[int].get" + IL_0044: stloc.s V_4 + IL_0046: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_004b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0050: stloc.s V_5 + IL_0052: ldloca.s V_6 + IL_0054: initobj "T" + IL_005a: ldloc.s V_6 + IL_005c: box "T" + IL_0061: brtrue.s IL_0067 + IL_0063: ldloca.s V_1 + IL_0065: br.s IL_0069 + IL_0067: ldloca.s V_0 + IL_0069: ldloc.3 + IL_006a: ldloc.s V_4 + IL_006c: ldloc.s V_5 + IL_006e: add + IL_006f: constrained. "T" + IL_0075: callvirt "void I1.this[int].set" + IL_007a: ret + } + """); } [Fact] @@ -6314,72 +6517,70 @@ public static void Main() CompileAndVerify(source, expectedOutput: expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (11,23): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // public async Task Run() - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "Run").WithArguments("TestCase.Run()").WithLocation(11, 23) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Run]: Return value missing on the stack. { Offset = 0x47 } - // [Bar]: Unexpected type on the stack. { Offset = 0xc, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run()", """ - // { - // // Code size 72 (0x48) - // .maxstack 2 - // .locals init (int V_0, //test - // int V_1, //count - // int V_2, - // TestCase V_3) - // IL_0000: ldc.i4.0 - // IL_0001: stloc.0 - // IL_0002: ldc.i4.0 - // IL_0003: stloc.1 - // .try - // { - // IL_0004: ldloc.0 - // IL_0005: ldc.i4.1 - // IL_0006: add - // IL_0007: stloc.0 - // IL_0008: ldloca.s V_3 - // IL_000a: initobj "TestCase" - // IL_0010: ldarg.0 - // IL_0011: call "System.Threading.Tasks.Task TestCase.Bar()" - // IL_0016: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_001b: stloc.2 - // IL_001c: ldloca.s V_3 - // IL_001e: ldloc.2 - // IL_001f: stfld "int TestCase.X" - // IL_0024: ldloc.3 - // IL_0025: ldfld "int TestCase.X" - // IL_002a: ldc.i4.1 - // IL_002b: bne.un.s IL_0031 - // IL_002d: ldloc.1 - // IL_002e: ldc.i4.1 - // IL_002f: add - // IL_0030: stloc.1 - // IL_0031: leave.s IL_0047 - // } - // finally - // { - // IL_0033: ldloc.0 - // IL_0034: ldloc.1 - // IL_0035: sub - // IL_0036: stsfld "int Driver.Result" - // IL_003b: ldsfld "System.Threading.AutoResetEvent Driver.CompleteSignal" - // IL_0040: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_0045: pop - // IL_0046: endfinally - // } - // IL_0047: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Run]: Return value missing on the stack. { Offset = 0x50 } + [Bar]: Unexpected type on the stack. { Offset = 0xc, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run()", """ + { + // Code size 81 (0x51) + .maxstack 2 + .locals init (TestCase V_0, + int V_1, //test + int V_2, //count + int V_3, + TestCase V_4) + IL_0000: ldarg.0 + IL_0001: ldobj "TestCase" + IL_0006: stloc.0 + IL_0007: ldc.i4.0 + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: stloc.2 + .try + { + IL_000b: ldloc.1 + IL_000c: ldc.i4.1 + IL_000d: add + IL_000e: stloc.1 + IL_000f: ldloca.s V_4 + IL_0011: initobj "TestCase" + IL_0017: ldloca.s V_0 + IL_0019: call "System.Threading.Tasks.Task TestCase.Bar()" + IL_001e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: stloc.3 + IL_0024: ldloca.s V_4 + IL_0026: ldloc.3 + IL_0027: stfld "int TestCase.X" + IL_002c: ldloc.s V_4 + IL_002e: ldfld "int TestCase.X" + IL_0033: ldc.i4.1 + IL_0034: bne.un.s IL_003a + IL_0036: ldloc.2 + IL_0037: ldc.i4.1 + IL_0038: add + IL_0039: stloc.2 + IL_003a: leave.s IL_0050 + } + finally + { + IL_003c: ldloc.1 + IL_003d: ldloc.2 + IL_003e: sub + IL_003f: stsfld "int Driver.Result" + IL_0044: ldsfld "System.Threading.AutoResetEvent Driver.CompleteSignal" + IL_0049: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_004e: pop + IL_004f: endfinally + } + IL_0050: ret + } + """); } [Fact] @@ -6511,10 +6712,12 @@ static void Main() Console.WriteLine(Driver.Result); } }"; - CompileAndVerify(source, "0"); + + var expectedOutput = "0"; + CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - var verifier = CompileAndVerify(comp, verify: Verification.Fails with + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with { ILVerifyMessage = """ [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } @@ -6700,91 +6903,93 @@ static void Main() CompileAndVerify(source, "0"); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (21,15): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // x[await GetVal(0)] += await GetVal(4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(0)").WithArguments("TestCase.Run()").WithLocation(21, 15) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // [Run]: Return value missing on the stack. { Offset = 0x6a } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run()", """ - // { - // // Code size 107 (0x6b) - // .maxstack 4 - // .locals init (int V_0, //tests - // int V_1, - // int V_2, - // int V_3) - // IL_0000: ldc.i4.0 - // IL_0001: stloc.0 - // .try - // { - // IL_0002: ldloc.0 - // IL_0003: ldc.i4.1 - // IL_0004: add - // IL_0005: stloc.0 - // IL_0006: ldc.i4.4 - // IL_0007: newarr "int" - // IL_000c: dup - // IL_000d: ldtoken ".__StaticArrayInitTypeSize=16 .CF97ADEEDB59E05BFD73A2B4C2A8885708C4F4F70C84C64B27120E72AB733B72" - // IL_0012: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" - // IL_0017: dup - // IL_0018: ldarg.0 - // IL_0019: ldc.i4.0 - // IL_001a: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0024: stloc.1 - // IL_0025: ldloc.1 - // IL_0026: ldelema "int" - // IL_002b: dup - // IL_002c: ldind.i4 - // IL_002d: stloc.2 - // IL_002e: ldarg.0 - // IL_002f: ldc.i4.4 - // IL_0030: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0035: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_003a: stloc.3 - // IL_003b: ldloc.2 - // IL_003c: ldloc.3 - // IL_003d: add - // IL_003e: stind.i4 - // IL_003f: ldc.i4.0 - // IL_0040: ldelem.i4 - // IL_0041: ldc.i4.5 - // IL_0042: bne.un.s IL_0050 - // IL_0044: ldsfld "int Driver.Count" - // IL_0049: ldc.i4.1 - // IL_004a: add - // IL_004b: stsfld "int Driver.Count" - // IL_0050: leave.s IL_006a - // } - // finally - // { - // IL_0052: ldsfld "int Driver.Count" - // IL_0057: ldloc.0 - // IL_0058: sub - // IL_0059: stsfld "int Driver.Result" - // IL_005e: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" - // IL_0063: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_0068: pop - // IL_0069: endfinally - // } - // IL_006a: ret - // } - // """); - } + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x73 } + """ + }); - [Fact] - public void SpillOperator_Compound2() - { + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run()", """ + { + // Code size 116 (0x74) + .maxstack 4 + .locals init (int V_0, + int V_1, //tests + int V_2, + int V_3, + int V_4) + IL_0000: ldc.i4.0 + IL_0001: stloc.1 + .try + { + IL_0002: ldloc.1 + IL_0003: ldc.i4.1 + IL_0004: add + IL_0005: stloc.1 + IL_0006: ldc.i4.4 + IL_0007: newarr "int" + IL_000c: dup + IL_000d: ldtoken ".__StaticArrayInitTypeSize=16 .CF97ADEEDB59E05BFD73A2B4C2A8885708C4F4F70C84C64B27120E72AB733B72" + IL_0012: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" + IL_0017: dup + IL_0018: ldarg.0 + IL_0019: ldc.i4.0 + IL_001a: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0024: stloc.2 + IL_0025: ldloc.2 + IL_0026: stloc.0 + IL_0027: dup + IL_0028: ldloc.0 + IL_0029: ldelem.i4 + IL_002a: pop + IL_002b: dup + IL_002c: ldloc.0 + IL_002d: ldelem.i4 + IL_002e: stloc.3 + IL_002f: ldarg.0 + IL_0030: ldc.i4.4 + IL_0031: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0036: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003b: stloc.s V_4 + IL_003d: ldloc.0 + IL_003e: ldelema "int" + IL_0043: ldloc.3 + IL_0044: ldloc.s V_4 + IL_0046: add + IL_0047: stind.i4 + IL_0048: ldc.i4.0 + IL_0049: ldelem.i4 + IL_004a: ldc.i4.5 + IL_004b: bne.un.s IL_0059 + IL_004d: ldsfld "int Driver.Count" + IL_0052: ldc.i4.1 + IL_0053: add + IL_0054: stsfld "int Driver.Count" + IL_0059: leave.s IL_0073 + } + finally + { + IL_005b: ldsfld "int Driver.Count" + IL_0060: ldloc.1 + IL_0061: sub + IL_0062: stsfld "int Driver.Result" + IL_0067: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_006c: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_0071: pop + IL_0072: endfinally + } + IL_0073: ret + } + """); + } + + [Fact] + public void SpillOperator_Compound2() + { var source = @" using System; using System.Threading; @@ -6896,86 +7101,88 @@ static void Main() CompileAndVerify(source, "0"); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (21,15): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // x[await GetVal(0)] += await GetVal(4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(0)").WithArguments("TestCase.Run()").WithLocation(21, 15) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // [Run]: Return value missing on the stack. { Offset = 0x6a } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run()", """ - // { - // // Code size 107 (0x6b) - // .maxstack 4 - // .locals init (int V_0, //tests - // int V_1, - // int V_2, - // int V_3) - // IL_0000: ldc.i4.0 - // IL_0001: stloc.0 - // .try - // { - // IL_0002: ldloc.0 - // IL_0003: ldc.i4.1 - // IL_0004: add - // IL_0005: stloc.0 - // IL_0006: ldc.i4.4 - // IL_0007: newarr "int" - // IL_000c: dup - // IL_000d: ldtoken ".__StaticArrayInitTypeSize=16 .CF97ADEEDB59E05BFD73A2B4C2A8885708C4F4F70C84C64B27120E72AB733B72" - // IL_0012: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" - // IL_0017: dup - // IL_0018: ldarg.0 - // IL_0019: ldc.i4.0 - // IL_001a: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0024: stloc.1 - // IL_0025: ldloc.1 - // IL_0026: ldelema "int" - // IL_002b: dup - // IL_002c: ldind.i4 - // IL_002d: stloc.2 - // IL_002e: ldarg.0 - // IL_002f: ldc.i4.4 - // IL_0030: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0035: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_003a: stloc.3 - // IL_003b: ldloc.2 - // IL_003c: ldloc.3 - // IL_003d: add - // IL_003e: stind.i4 - // IL_003f: ldc.i4.0 - // IL_0040: ldelem.i4 - // IL_0041: ldc.i4.5 - // IL_0042: bne.un.s IL_0050 - // IL_0044: ldsfld "int Driver.Count" - // IL_0049: ldc.i4.1 - // IL_004a: add - // IL_004b: stsfld "int Driver.Count" - // IL_0050: leave.s IL_006a - // } - // finally - // { - // IL_0052: ldsfld "int Driver.Count" - // IL_0057: ldloc.0 - // IL_0058: sub - // IL_0059: stsfld "int Driver.Result" - // IL_005e: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" - // IL_0063: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_0068: pop - // IL_0069: endfinally - // } - // IL_006a: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x73 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run()", """ + { + // Code size 116 (0x74) + .maxstack 4 + .locals init (int V_0, + int V_1, //tests + int V_2, + int V_3, + int V_4) + IL_0000: ldc.i4.0 + IL_0001: stloc.1 + .try + { + IL_0002: ldloc.1 + IL_0003: ldc.i4.1 + IL_0004: add + IL_0005: stloc.1 + IL_0006: ldc.i4.4 + IL_0007: newarr "int" + IL_000c: dup + IL_000d: ldtoken ".__StaticArrayInitTypeSize=16 .CF97ADEEDB59E05BFD73A2B4C2A8885708C4F4F70C84C64B27120E72AB733B72" + IL_0012: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" + IL_0017: dup + IL_0018: ldarg.0 + IL_0019: ldc.i4.0 + IL_001a: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0024: stloc.2 + IL_0025: ldloc.2 + IL_0026: stloc.0 + IL_0027: dup + IL_0028: ldloc.0 + IL_0029: ldelem.i4 + IL_002a: pop + IL_002b: dup + IL_002c: ldloc.0 + IL_002d: ldelem.i4 + IL_002e: stloc.3 + IL_002f: ldarg.0 + IL_0030: ldc.i4.4 + IL_0031: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0036: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003b: stloc.s V_4 + IL_003d: ldloc.0 + IL_003e: ldelema "int" + IL_0043: ldloc.3 + IL_0044: ldloc.s V_4 + IL_0046: add + IL_0047: stind.i4 + IL_0048: ldc.i4.0 + IL_0049: ldelem.i4 + IL_004a: ldc.i4.5 + IL_004b: bne.un.s IL_0059 + IL_004d: ldsfld "int Driver.Count" + IL_0052: ldc.i4.1 + IL_0053: add + IL_0054: stsfld "int Driver.Count" + IL_0059: leave.s IL_0073 + } + finally + { + IL_005b: ldsfld "int Driver.Count" + IL_0060: ldloc.1 + IL_0061: sub + IL_0062: stsfld "int Driver.Result" + IL_0067: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_006c: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_0071: pop + IL_0072: endfinally + } + IL_0073: ret + } + """); } [Fact] @@ -7133,114 +7340,120 @@ static void Main() CompileAndVerify(source, "0"); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (15,23): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // public async Task Run() - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "Run").WithArguments("TestCase.Run()").WithLocation(15, 23), - // (23,21): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // val = x[await GetVal(0)] += await GetVal(4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(0)").WithArguments("TestCase.Run()").WithLocation(23, 21) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // [Run]: Return value missing on the stack. { Offset = 0x9d } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run()", """ - // { - // // Code size 158 (0x9e) - // .maxstack 5 - // .locals init (int V_0, //tests - // int V_1, - // int& V_2, - // int V_3, - // int V_4, - // int V_5, - // bool V_6) - // IL_0000: ldc.i4.0 - // IL_0001: stloc.0 - // .try - // { - // IL_0002: ldloc.0 - // IL_0003: ldc.i4.1 - // IL_0004: add - // IL_0005: stloc.0 - // IL_0006: ldc.i4.4 - // IL_0007: newarr "int" - // IL_000c: dup - // IL_000d: ldtoken ".__StaticArrayInitTypeSize=16 .CF97ADEEDB59E05BFD73A2B4C2A8885708C4F4F70C84C64B27120E72AB733B72" - // IL_0012: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" - // IL_0017: dup - // IL_0018: ldarg.0 - // IL_0019: ldc.i4.0 - // IL_001a: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0024: stloc.1 - // IL_0025: ldloc.1 - // IL_0026: ldelema "int" - // IL_002b: stloc.2 - // IL_002c: ldloc.2 - // IL_002d: ldind.i4 - // IL_002e: stloc.3 - // IL_002f: ldarg.0 - // IL_0030: ldc.i4.4 - // IL_0031: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0036: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_003b: stloc.s V_4 - // IL_003d: ldarg.0 - // IL_003e: ldloc.2 - // IL_003f: ldloc.3 - // IL_0040: ldloc.s V_4 - // IL_0042: add - // IL_0043: dup - // IL_0044: stloc.s V_5 - // IL_0046: stind.i4 - // IL_0047: ldloc.s V_5 - // IL_0049: stfld "int TestCase.val" - // IL_004e: ldc.i4.0 - // IL_004f: ldelem.i4 - // IL_0050: ldc.i4.5 - // IL_0051: ceq - // IL_0053: stloc.s V_6 - // IL_0055: ldloc.s V_6 - // IL_0057: brfalse.s IL_0073 - // IL_0059: ldarg.0 - // IL_005a: ldfld "int TestCase.val" - // IL_005f: ldarg.0 - // IL_0060: ldc.i4.5 - // IL_0061: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0066: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_006b: stloc.s V_4 - // IL_006d: ldloc.s V_4 - // IL_006f: ceq - // IL_0071: stloc.s V_6 - // IL_0073: ldloc.s V_6 - // IL_0075: brfalse.s IL_0083 - // IL_0077: ldsfld "int Driver.Count" - // IL_007c: ldc.i4.1 - // IL_007d: add - // IL_007e: stsfld "int Driver.Count" - // IL_0083: leave.s IL_009d - // } - // finally - // { - // IL_0085: ldsfld "int Driver.Count" - // IL_008a: ldloc.0 - // IL_008b: sub - // IL_008c: stsfld "int Driver.Result" - // IL_0091: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" - // IL_0096: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_009b: pop - // IL_009c: endfinally - // } - // IL_009d: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0xb7 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run()", """ + { + // Code size 184 (0xb8) + .maxstack 5 + .locals init (TestCase V_0, + int[] V_1, + int V_2, + int V_3, //tests + int[] V_4, + int& V_5, + int V_6, + int V_7, + int V_8, + bool V_9) + IL_0000: ldarg.0 + IL_0001: ldobj "TestCase" + IL_0006: stloc.0 + IL_0007: ldc.i4.0 + IL_0008: stloc.3 + .try + { + IL_0009: ldloc.3 + IL_000a: ldc.i4.1 + IL_000b: add + IL_000c: stloc.3 + IL_000d: ldc.i4.4 + IL_000e: newarr "int" + IL_0013: dup + IL_0014: ldtoken ".__StaticArrayInitTypeSize=16 .CF97ADEEDB59E05BFD73A2B4C2A8885708C4F4F70C84C64B27120E72AB733B72" + IL_0019: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" + IL_001e: dup + IL_001f: stloc.s V_4 + IL_0021: ldloca.s V_0 + IL_0023: ldc.i4.0 + IL_0024: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0029: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002e: ldloc.s V_4 + IL_0030: stloc.1 + IL_0031: stloc.2 + IL_0032: ldloc.1 + IL_0033: ldloc.2 + IL_0034: ldelem.i4 + IL_0035: pop + IL_0036: ldloc.1 + IL_0037: ldloc.2 + IL_0038: ldelem.i4 + IL_0039: stloc.s V_6 + IL_003b: ldloca.s V_0 + IL_003d: ldc.i4.4 + IL_003e: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0043: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0048: stloc.s V_7 + IL_004a: ldloca.s V_0 + IL_004c: ldloc.1 + IL_004d: ldloc.2 + IL_004e: ldelema "int" + IL_0053: stloc.s V_5 + IL_0055: ldloc.s V_5 + IL_0057: ldloc.s V_6 + IL_0059: ldloc.s V_7 + IL_005b: add + IL_005c: dup + IL_005d: stloc.s V_8 + IL_005f: stind.i4 + IL_0060: ldloc.s V_8 + IL_0062: stfld "int TestCase.val" + IL_0067: ldc.i4.0 + IL_0068: ldelem.i4 + IL_0069: ldc.i4.5 + IL_006a: ceq + IL_006c: stloc.s V_9 + IL_006e: ldloc.s V_9 + IL_0070: brfalse.s IL_008d + IL_0072: ldloc.0 + IL_0073: ldfld "int TestCase.val" + IL_0078: ldloca.s V_0 + IL_007a: ldc.i4.5 + IL_007b: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0080: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0085: stloc.s V_7 + IL_0087: ldloc.s V_7 + IL_0089: ceq + IL_008b: stloc.s V_9 + IL_008d: ldloc.s V_9 + IL_008f: brfalse.s IL_009d + IL_0091: ldsfld "int Driver.Count" + IL_0096: ldc.i4.1 + IL_0097: add + IL_0098: stsfld "int Driver.Count" + IL_009d: leave.s IL_00b7 + } + finally + { + IL_009f: ldsfld "int Driver.Count" + IL_00a4: ldloc.3 + IL_00a5: sub + IL_00a6: stsfld "int Driver.Result" + IL_00ab: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_00b0: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_00b5: pop + IL_00b6: endfinally + } + IL_00b7: ret + } + """); } [Fact] @@ -7521,10 +7734,12 @@ static void Main() Console.WriteLine(t.Result); } }"; - CompileAndVerify(source, "42"); + + var expectedOutput = "42"; + CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - var verifier = CompileAndVerify(comp, verify: Verification.Fails with + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with { ILVerifyMessage = """ [Run]: Unexpected type on the stack. { Offset = 0x47, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } @@ -7732,200 +7947,211 @@ static void Main() CompileAndVerify(source, "0"); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (39,42): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // this.myClass.arr[0].Field += await GetVal(4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(4)").WithArguments("TestCase.Run()").WithLocation(39, 42), - // (39,42): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // this.myClass.arr[0].Field += await GetVal(4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(4)").WithArguments("TestCase.Run()").WithLocation(39, 42), - // (44,30): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // this.myClass.arr[await GetVal(1)].Field += await GetVal(4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(1)").WithArguments("TestCase.Run()").WithLocation(44, 30), - // (44,30): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // this.myClass.arr[await GetVal(1)].Field += await GetVal(4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(1)").WithArguments("TestCase.Run()").WithLocation(44, 30) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // [Run]: Return value missing on the stack. { Offset = 0x182 } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run()", """ - // { - // // Code size 387 (0x183) - // .maxstack 3 - // .locals init (int V_0, //tests - // int V_1, - // int V_2, - // int V_3) - // IL_0000: ldc.i4.0 - // IL_0001: stloc.0 - // IL_0002: ldarg.0 - // IL_0003: newobj "TestCase.PrivClass..ctor()" - // IL_0008: stfld "TestCase.PrivClass TestCase.myClass" - // .try - // { - // IL_000d: ldloc.0 - // IL_000e: ldc.i4.1 - // IL_000f: add - // IL_0010: stloc.0 - // IL_0011: ldarg.0 - // IL_0012: ldfld "TestCase.PrivClass TestCase.myClass" - // IL_0017: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" - // IL_001c: dup - // IL_001d: ldc.i4.0 - // IL_001e: ldelema "TestCase.PrivClass.ValueT" - // IL_0023: pop - // IL_0024: ldarg.0 - // IL_0025: ldc.i4.4 - // IL_0026: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_002b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0030: stloc.1 - // IL_0031: ldc.i4.0 - // IL_0032: ldelema "TestCase.PrivClass.ValueT" - // IL_0037: ldloc.1 - // IL_0038: stfld "int TestCase.PrivClass.ValueT.Field" - // IL_003d: ldarg.0 - // IL_003e: ldfld "TestCase.PrivClass TestCase.myClass" - // IL_0043: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" - // IL_0048: ldc.i4.0 - // IL_0049: ldelema "TestCase.PrivClass.ValueT" - // IL_004e: ldfld "int TestCase.PrivClass.ValueT.Field" - // IL_0053: ldc.i4.4 - // IL_0054: bne.un.s IL_0062 - // IL_0056: ldsfld "int Driver.Count" - // IL_005b: ldc.i4.1 - // IL_005c: add - // IL_005d: stsfld "int Driver.Count" - // IL_0062: ldloc.0 - // IL_0063: ldc.i4.1 - // IL_0064: add - // IL_0065: stloc.0 - // IL_0066: ldarg.0 - // IL_0067: ldfld "TestCase.PrivClass TestCase.myClass" - // IL_006c: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" - // IL_0071: ldc.i4.0 - // IL_0072: ldelema "TestCase.PrivClass.ValueT" - // IL_0077: ldflda "int TestCase.PrivClass.ValueT.Field" - // IL_007c: dup - // IL_007d: ldind.i4 - // IL_007e: stloc.1 - // IL_007f: ldarg.0 - // IL_0080: ldc.i4.4 - // IL_0081: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0086: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_008b: stloc.2 - // IL_008c: ldloc.1 - // IL_008d: ldloc.2 - // IL_008e: add - // IL_008f: stind.i4 - // IL_0090: ldarg.0 - // IL_0091: ldfld "TestCase.PrivClass TestCase.myClass" - // IL_0096: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" - // IL_009b: ldc.i4.0 - // IL_009c: ldelema "TestCase.PrivClass.ValueT" - // IL_00a1: ldfld "int TestCase.PrivClass.ValueT.Field" - // IL_00a6: ldc.i4.8 - // IL_00a7: bne.un.s IL_00b5 - // IL_00a9: ldsfld "int Driver.Count" - // IL_00ae: ldc.i4.1 - // IL_00af: add - // IL_00b0: stsfld "int Driver.Count" - // IL_00b5: ldloc.0 - // IL_00b6: ldc.i4.1 - // IL_00b7: add - // IL_00b8: stloc.0 - // IL_00b9: ldarg.0 - // IL_00ba: ldfld "TestCase.PrivClass TestCase.myClass" - // IL_00bf: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" - // IL_00c4: ldarg.0 - // IL_00c5: ldc.i4.1 - // IL_00c6: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_00cb: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_00d0: stloc.2 - // IL_00d1: ldloc.2 - // IL_00d2: ldelema "TestCase.PrivClass.ValueT" - // IL_00d7: ldflda "int TestCase.PrivClass.ValueT.Field" - // IL_00dc: dup - // IL_00dd: ldind.i4 - // IL_00de: stloc.1 - // IL_00df: ldarg.0 - // IL_00e0: ldc.i4.4 - // IL_00e1: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_00e6: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_00eb: stloc.3 - // IL_00ec: ldloc.1 - // IL_00ed: ldloc.3 - // IL_00ee: add - // IL_00ef: stind.i4 - // IL_00f0: ldarg.0 - // IL_00f1: ldfld "TestCase.PrivClass TestCase.myClass" - // IL_00f6: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" - // IL_00fb: ldc.i4.1 - // IL_00fc: ldelema "TestCase.PrivClass.ValueT" - // IL_0101: ldfld "int TestCase.PrivClass.ValueT.Field" - // IL_0106: ldc.i4.4 - // IL_0107: bne.un.s IL_0115 - // IL_0109: ldsfld "int Driver.Count" - // IL_010e: ldc.i4.1 - // IL_010f: add - // IL_0110: stsfld "int Driver.Count" - // IL_0115: ldloc.0 - // IL_0116: ldc.i4.1 - // IL_0117: add - // IL_0118: stloc.0 - // IL_0119: ldarg.0 - // IL_011a: ldfld "TestCase.PrivClass TestCase.myClass" - // IL_011f: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" - // IL_0124: ldarg.0 - // IL_0125: ldc.i4.1 - // IL_0126: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_012b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0130: stloc.3 - // IL_0131: ldloc.3 - // IL_0132: ldelema "TestCase.PrivClass.ValueT" - // IL_0137: ldflda "int TestCase.PrivClass.ValueT.Field" - // IL_013c: dup - // IL_013d: ldind.i4 - // IL_013e: stloc.1 - // IL_013f: ldloc.1 - // IL_0140: ldc.i4.1 - // IL_0141: add - // IL_0142: stind.i4 - // IL_0143: ldarg.0 - // IL_0144: ldfld "TestCase.PrivClass TestCase.myClass" - // IL_0149: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" - // IL_014e: ldc.i4.1 - // IL_014f: ldelema "TestCase.PrivClass.ValueT" - // IL_0154: ldfld "int TestCase.PrivClass.ValueT.Field" - // IL_0159: ldc.i4.5 - // IL_015a: bne.un.s IL_0168 - // IL_015c: ldsfld "int Driver.Count" - // IL_0161: ldc.i4.1 - // IL_0162: add - // IL_0163: stsfld "int Driver.Count" - // IL_0168: leave.s IL_0182 - // } - // finally - // { - // IL_016a: ldsfld "int Driver.Count" - // IL_016f: ldloc.0 - // IL_0170: sub - // IL_0171: stsfld "int Driver.Result" - // IL_0176: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" - // IL_017b: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_0180: pop - // IL_0181: endfinally - // } - // IL_0182: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x1c2 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run()", """ + { + // Code size 451 (0x1c3) + .maxstack 3 + .locals init (int V_0, + int V_1, + int V_2, //tests + int V_3, + int V_4, + int V_5) + IL_0000: ldc.i4.0 + IL_0001: stloc.2 + IL_0002: ldarg.0 + IL_0003: newobj "TestCase.PrivClass..ctor()" + IL_0008: stfld "TestCase.PrivClass TestCase.myClass" + .try + { + IL_000d: ldloc.2 + IL_000e: ldc.i4.1 + IL_000f: add + IL_0010: stloc.2 + IL_0011: ldarg.0 + IL_0012: ldfld "TestCase.PrivClass TestCase.myClass" + IL_0017: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + IL_001c: dup + IL_001d: ldc.i4.0 + IL_001e: ldelema "TestCase.PrivClass.ValueT" + IL_0023: pop + IL_0024: ldarg.0 + IL_0025: ldc.i4.4 + IL_0026: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_002b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0030: stloc.3 + IL_0031: ldc.i4.0 + IL_0032: ldelema "TestCase.PrivClass.ValueT" + IL_0037: ldloc.3 + IL_0038: stfld "int TestCase.PrivClass.ValueT.Field" + IL_003d: ldarg.0 + IL_003e: ldfld "TestCase.PrivClass TestCase.myClass" + IL_0043: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + IL_0048: ldc.i4.0 + IL_0049: ldelema "TestCase.PrivClass.ValueT" + IL_004e: ldfld "int TestCase.PrivClass.ValueT.Field" + IL_0053: ldc.i4.4 + IL_0054: bne.un.s IL_0062 + IL_0056: ldsfld "int Driver.Count" + IL_005b: ldc.i4.1 + IL_005c: add + IL_005d: stsfld "int Driver.Count" + IL_0062: ldloc.2 + IL_0063: ldc.i4.1 + IL_0064: add + IL_0065: stloc.2 + IL_0066: ldarg.0 + IL_0067: ldfld "TestCase.PrivClass TestCase.myClass" + IL_006c: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + IL_0071: dup + IL_0072: ldc.i4.0 + IL_0073: ldelema "TestCase.PrivClass.ValueT" + IL_0078: pop + IL_0079: dup + IL_007a: ldc.i4.0 + IL_007b: ldelema "TestCase.PrivClass.ValueT" + IL_0080: pop + IL_0081: dup + IL_0082: ldc.i4.0 + IL_0083: ldelema "TestCase.PrivClass.ValueT" + IL_0088: ldfld "int TestCase.PrivClass.ValueT.Field" + IL_008d: stloc.3 + IL_008e: ldarg.0 + IL_008f: ldc.i4.4 + IL_0090: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0095: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_009a: stloc.s V_4 + IL_009c: ldc.i4.0 + IL_009d: ldelema "TestCase.PrivClass.ValueT" + IL_00a2: ldflda "int TestCase.PrivClass.ValueT.Field" + IL_00a7: ldloc.3 + IL_00a8: ldloc.s V_4 + IL_00aa: add + IL_00ab: stind.i4 + IL_00ac: ldarg.0 + IL_00ad: ldfld "TestCase.PrivClass TestCase.myClass" + IL_00b2: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + IL_00b7: ldc.i4.0 + IL_00b8: ldelema "TestCase.PrivClass.ValueT" + IL_00bd: ldfld "int TestCase.PrivClass.ValueT.Field" + IL_00c2: ldc.i4.8 + IL_00c3: bne.un.s IL_00d1 + IL_00c5: ldsfld "int Driver.Count" + IL_00ca: ldc.i4.1 + IL_00cb: add + IL_00cc: stsfld "int Driver.Count" + IL_00d1: ldloc.2 + IL_00d2: ldc.i4.1 + IL_00d3: add + IL_00d4: stloc.2 + IL_00d5: ldarg.0 + IL_00d6: ldfld "TestCase.PrivClass TestCase.myClass" + IL_00db: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + IL_00e0: ldarg.0 + IL_00e1: ldc.i4.1 + IL_00e2: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_00e7: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_00ec: stloc.s V_4 + IL_00ee: ldloc.s V_4 + IL_00f0: stloc.0 + IL_00f1: dup + IL_00f2: ldloc.0 + IL_00f3: ldelema "TestCase.PrivClass.ValueT" + IL_00f8: pop + IL_00f9: ldloc.0 + IL_00fa: stloc.1 + IL_00fb: dup + IL_00fc: ldloc.1 + IL_00fd: ldelema "TestCase.PrivClass.ValueT" + IL_0102: pop + IL_0103: dup + IL_0104: ldloc.1 + IL_0105: ldelema "TestCase.PrivClass.ValueT" + IL_010a: ldfld "int TestCase.PrivClass.ValueT.Field" + IL_010f: stloc.3 + IL_0110: ldarg.0 + IL_0111: ldc.i4.4 + IL_0112: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0117: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_011c: stloc.s V_5 + IL_011e: ldloc.1 + IL_011f: ldelema "TestCase.PrivClass.ValueT" + IL_0124: ldflda "int TestCase.PrivClass.ValueT.Field" + IL_0129: ldloc.3 + IL_012a: ldloc.s V_5 + IL_012c: add + IL_012d: stind.i4 + IL_012e: ldarg.0 + IL_012f: ldfld "TestCase.PrivClass TestCase.myClass" + IL_0134: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + IL_0139: ldc.i4.1 + IL_013a: ldelema "TestCase.PrivClass.ValueT" + IL_013f: ldfld "int TestCase.PrivClass.ValueT.Field" + IL_0144: ldc.i4.4 + IL_0145: bne.un.s IL_0153 + IL_0147: ldsfld "int Driver.Count" + IL_014c: ldc.i4.1 + IL_014d: add + IL_014e: stsfld "int Driver.Count" + IL_0153: ldloc.2 + IL_0154: ldc.i4.1 + IL_0155: add + IL_0156: stloc.2 + IL_0157: ldarg.0 + IL_0158: ldfld "TestCase.PrivClass TestCase.myClass" + IL_015d: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + IL_0162: ldarg.0 + IL_0163: ldc.i4.1 + IL_0164: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0169: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_016e: stloc.s V_5 + IL_0170: ldloc.s V_5 + IL_0172: ldelema "TestCase.PrivClass.ValueT" + IL_0177: ldflda "int TestCase.PrivClass.ValueT.Field" + IL_017c: dup + IL_017d: ldind.i4 + IL_017e: stloc.3 + IL_017f: ldloc.3 + IL_0180: ldc.i4.1 + IL_0181: add + IL_0182: stind.i4 + IL_0183: ldarg.0 + IL_0184: ldfld "TestCase.PrivClass TestCase.myClass" + IL_0189: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + IL_018e: ldc.i4.1 + IL_018f: ldelema "TestCase.PrivClass.ValueT" + IL_0194: ldfld "int TestCase.PrivClass.ValueT.Field" + IL_0199: ldc.i4.5 + IL_019a: bne.un.s IL_01a8 + IL_019c: ldsfld "int Driver.Count" + IL_01a1: ldc.i4.1 + IL_01a2: add + IL_01a3: stsfld "int Driver.Count" + IL_01a8: leave.s IL_01c2 + } + finally + { + IL_01aa: ldsfld "int Driver.Count" + IL_01af: ldloc.2 + IL_01b0: sub + IL_01b1: stsfld "int Driver.Result" + IL_01b6: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_01bb: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_01c0: pop + IL_01c1: endfinally + } + IL_01c2: ret + } + """); } [Fact, WorkItem(36443, "https://github.com/dotnet/roslyn/issues/36443")] @@ -7951,63 +8177,60 @@ static async Task Main() CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (11,34): error CS9328: Method 'S.Main()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // Console.WriteLine(s.i += await GetInt()); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetInt()").WithArguments("S.Main()").WithLocation(11, 34) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Main]: Return value missing on the stack. { Offset = 0x63 } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("S.Main()", """ - // { - // // Code size 100 (0x64) - // .maxstack 3 - // .locals init (S V_0, //s - // int? V_1, - // int? V_2, - // int? V_3) - // IL_0000: ldloca.s V_0 - // IL_0002: initobj "S" - // IL_0008: ldloca.s V_0 - // IL_000a: ldflda "int? S.i" - // IL_000f: dup - // IL_0010: ldobj "int?" - // IL_0015: stloc.1 - // IL_0016: call "System.Threading.Tasks.Task S.GetInt()" - // IL_001b: call "int? System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0020: stloc.2 - // IL_0021: ldloca.s V_1 - // IL_0023: call "readonly bool int?.HasValue.get" - // IL_0028: ldloca.s V_2 - // IL_002a: call "readonly bool int?.HasValue.get" - // IL_002f: and - // IL_0030: brtrue.s IL_003d - // IL_0032: ldloca.s V_3 - // IL_0034: initobj "int?" - // IL_003a: ldloc.3 - // IL_003b: br.s IL_0051 - // IL_003d: ldloca.s V_1 - // IL_003f: call "readonly int int?.GetValueOrDefault()" - // IL_0044: ldloca.s V_2 - // IL_0046: call "readonly int int?.GetValueOrDefault()" - // IL_004b: add - // IL_004c: newobj "int?..ctor(int)" - // IL_0051: dup - // IL_0052: stloc.3 - // IL_0053: stobj "int?" - // IL_0058: ldloc.3 - // IL_0059: box "int?" - // IL_005e: call "void System.Console.WriteLine(object)" - // IL_0063: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x68 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.Main()", """ + { + // Code size 105 (0x69) + .maxstack 3 + .locals init (S V_0, + S V_1, + int? V_2, + int? V_3, + int? V_4) + IL_0000: ldloca.s V_1 + IL_0002: initobj "S" + IL_0008: ldloc.1 + IL_0009: stloc.0 + IL_000a: ldloc.0 + IL_000b: ldfld "int? S.i" + IL_0010: stloc.2 + IL_0011: call "System.Threading.Tasks.Task S.GetInt()" + IL_0016: call "int? System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001b: stloc.3 + IL_001c: ldloca.s V_0 + IL_001e: ldflda "int? S.i" + IL_0023: ldloca.s V_2 + IL_0025: call "readonly bool int?.HasValue.get" + IL_002a: ldloca.s V_3 + IL_002c: call "readonly bool int?.HasValue.get" + IL_0031: and + IL_0032: brtrue.s IL_0040 + IL_0034: ldloca.s V_4 + IL_0036: initobj "int?" + IL_003c: ldloc.s V_4 + IL_003e: br.s IL_0054 + IL_0040: ldloca.s V_2 + IL_0042: call "readonly int int?.GetValueOrDefault()" + IL_0047: ldloca.s V_3 + IL_0049: call "readonly int int?.GetValueOrDefault()" + IL_004e: add + IL_004f: newobj "int?..ctor(int)" + IL_0054: dup + IL_0055: stloc.s V_4 + IL_0057: stobj "int?" + IL_005c: ldloc.s V_4 + IL_005e: box "int?" + IL_0063: call "void System.Console.WriteLine(object)" + IL_0068: ret + } + """); } [Fact, WorkItem(36443, "https://github.com/dotnet/roslyn/issues/36443")] @@ -8185,196 +8408,1419 @@ static async Task Main() CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (11,34): error CS9328: Method 'S.M(S)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // Console.WriteLine(s.i += await GetInt()); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetInt()").WithArguments("S.M(S)").WithLocation(11, 34), - // (16,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. - // M(); - Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "M()").WithLocation(16, 9), + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x65 } + [Main]: Return value missing on the stack. { Offset = 0xf } + """ + }); + + verifier.VerifyDiagnostics( // (14,23): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. // static async Task Main() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(14, 23) + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(14, 23), + // (16,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. + // M(); + Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "M()").WithLocation(16, 9) ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [M]: Return value missing on the stack. { Offset = 0x63 } - // [Main]: Return value missing on the stack. { Offset = 0xf } - // """ - // }); - - // verifier.VerifyDiagnostics( - // // (14,23): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. - // // static async Task Main() - // Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(14, 23), - // // (16,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. - // // M(); - // Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "M()").WithLocation(16, 9) - // ); - // verifier.VerifyIL("S.M(S)", """ - // { - // // Code size 100 (0x64) - // .maxstack 3 - // .locals init (int? V_0, - // int? V_1, - // int? V_2) - // IL_0000: ldarga.s V_0 - // IL_0002: initobj "S" - // IL_0008: ldarga.s V_0 - // IL_000a: ldflda "int? S.i" - // IL_000f: dup - // IL_0010: ldobj "int?" - // IL_0015: stloc.0 - // IL_0016: call "System.Threading.Tasks.Task S.GetInt()" - // IL_001b: call "int? System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0020: stloc.1 - // IL_0021: ldloca.s V_0 - // IL_0023: call "readonly bool int?.HasValue.get" - // IL_0028: ldloca.s V_1 - // IL_002a: call "readonly bool int?.HasValue.get" - // IL_002f: and - // IL_0030: brtrue.s IL_003d - // IL_0032: ldloca.s V_2 - // IL_0034: initobj "int?" - // IL_003a: ldloc.2 - // IL_003b: br.s IL_0051 - // IL_003d: ldloca.s V_0 - // IL_003f: call "readonly int int?.GetValueOrDefault()" - // IL_0044: ldloca.s V_1 - // IL_0046: call "readonly int int?.GetValueOrDefault()" - // IL_004b: add - // IL_004c: newobj "int?..ctor(int)" - // IL_0051: dup - // IL_0052: stloc.2 - // IL_0053: stobj "int?" - // IL_0058: ldloc.2 - // IL_0059: box "int?" - // IL_005e: call "void System.Console.WriteLine(object)" - // IL_0063: ret - // } - // """); + verifier.VerifyIL("S.M(S)", """ + { + // Code size 102 (0x66) + .maxstack 3 + .locals init (S V_0, + int? V_1, + int? V_2, + int? V_3) + IL_0000: ldarga.s V_0 + IL_0002: initobj "S" + IL_0008: ldarg.0 + IL_0009: stloc.0 + IL_000a: ldloc.0 + IL_000b: ldfld "int? S.i" + IL_0010: stloc.1 + IL_0011: call "System.Threading.Tasks.Task S.GetInt()" + IL_0016: call "int? System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001b: stloc.2 + IL_001c: ldloca.s V_0 + IL_001e: ldflda "int? S.i" + IL_0023: ldloca.s V_1 + IL_0025: call "readonly bool int?.HasValue.get" + IL_002a: ldloca.s V_2 + IL_002c: call "readonly bool int?.HasValue.get" + IL_0031: and + IL_0032: brtrue.s IL_003f + IL_0034: ldloca.s V_3 + IL_0036: initobj "int?" + IL_003c: ldloc.3 + IL_003d: br.s IL_0053 + IL_003f: ldloca.s V_1 + IL_0041: call "readonly int int?.GetValueOrDefault()" + IL_0046: ldloca.s V_2 + IL_0048: call "readonly int int?.GetValueOrDefault()" + IL_004d: add + IL_004e: newobj "int?..ctor(int)" + IL_0053: dup + IL_0054: stloc.3 + IL_0055: stobj "int?" + IL_005a: ldloc.3 + IL_005b: box "int?" + IL_0060: call "void System.Console.WriteLine(object)" + IL_0065: ret + } + """); } [Fact] - public void SpillSacrificialRead() + public void SpillAssignmentToThisStruct_01() { - var source = @" -using System; -using System.Threading.Tasks; - -class C -{ - static void F1(ref int x, int y, int z) - { - x += y + z; - } + var source = """ + using System; + using System.Threading.Tasks; + struct S + { + int i; - static int F0() - { - Console.WriteLine(-1); - return 0; - } + async Task M() + { + i = 1; // Not observable outside of the method + return; + } - static async Task F2() - { - int[] x = new int[1] { 21 }; - x = null; - F1(ref x[0], F0(), await Task.Factory.StartNew(() => 21)); - return x[0]; - } + static async Task Main() + { + S s = default; + await s.M(); + Console.Write(s.i); + } - public static void Main() - { - var t = F2(); - try - { - t.Wait(); - } - catch(Exception) - { - Console.WriteLine(0); - return; - } + static Task GetInt() => Task.FromResult(1); + } + """; - Console.WriteLine(-1); - } -}"; - CompileAndVerify(source, "0"); + var expectedOutput = "0"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (22,28): error CS9328: Method 'C.F2()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // F1(ref x[0], F0(), await Task.Factory.StartNew(() => 21)); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 21)").WithArguments("C.F2()").WithLocation(22, 28) + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0xf } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + + verifier.VerifyDiagnostics( + // (7,16): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // async Task M() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(7, 16) ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [F2]: Unexpected type on the stack. { Offset = 0x52, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("C.F2()", """ - // { - // // Code size 83 (0x53) - // .maxstack 5 - // .locals init (int V_0, - // int V_1) - // IL_0000: ldc.i4.1 - // IL_0001: newarr "int" - // IL_0006: dup - // IL_0007: ldc.i4.0 - // IL_0008: ldc.i4.s 21 - // IL_000a: stelem.i4 - // IL_000b: pop - // IL_000c: ldnull - // IL_000d: dup - // IL_000e: ldc.i4.0 - // IL_000f: ldelema "int" - // IL_0014: call "int C.F0()" - // IL_0019: stloc.0 - // IL_001a: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_001f: ldsfld "System.Func C.<>c.<>9__2_0" - // IL_0024: dup - // IL_0025: brtrue.s IL_003e - // IL_0027: pop - // IL_0028: ldsfld "C.<>c C.<>c.<>9" - // IL_002d: ldftn "int C.<>c.b__2_0()" - // IL_0033: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_0038: dup - // IL_0039: stsfld "System.Func C.<>c.<>9__2_0" - // IL_003e: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_0043: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0048: stloc.1 - // IL_0049: ldloc.0 - // IL_004a: ldloc.1 - // IL_004b: call "void C.F1(ref int, int, int)" - // IL_0050: ldc.i4.0 - // IL_0051: ldelem.i4 - // IL_0052: ret - // } - // """); + verifier.VerifyIL("S.M()", """ + { + // Code size 16 (0x10) + .maxstack 2 + .locals init (S V_0) + IL_0000: ldarg.0 + IL_0001: ldobj "S" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: ldc.i4.1 + IL_000a: stfld "int S.i" + IL_000f: ret + } + """); } [Fact] - public void SpillRefThisStruct() + public void SpillAssignmentToThisStruct_02() { - var source = @" -using System; -using System.Threading.Tasks; + var source = """ + using System; + using System.Threading.Tasks; + struct S : I + { + public int P { get; set; } -struct s1 -{ - public int X; + static async Task Main() + { + S s = default; + await s.M(); + Console.Write(s.P); + } - public async void Goo1() - { - Bar(ref this, await Task.FromResult(42)); - } + static Task GetInt() => Task.FromResult(1); + } + + interface I + { + public int P { get; set; } + } + + static class Extensions + { + extension(T t) where T : I + { + public async Task M() + { + t.P = 1; + return; + } + } + } + """; + + var expectedOutput = "0"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x1f } + [M]: Return value missing on the stack. { Offset = 0xe } + """ + }); + + verifier.VerifyDiagnostics( + // (26,27): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // public async Task M() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(26, 27) + ); + verifier.VerifyIL("Extensions.M(this T)", """ + { + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldarga.s V_0 + IL_0002: ldc.i4.1 + IL_0003: constrained. "T" + IL_0009: callvirt "void I.P.set" + IL_000e: ret + } + """); + } + + [Fact] + public void SpillAssignmentToThisStruct_03() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S : I + { + public int P { get; set; } + + static async Task Main() + { + S s = default; + await s.M(); + Console.Write(s.P); + } + + static Task GetInt() => Task.FromResult(1); + } + + interface I + { + public int P { get; set; } + } + + static class Extensions + { + extension(ref T t) where T : struct, I + { + public async Task M() + { + t.P = 1; + return; + } + } + } + """; + + var expectedDiagnostics = new[] { + // (26,27): error CS1988: Async methods cannot have ref, in or out parameters + // public async Task M() + Diagnostic(ErrorCode.ERR_BadAsyncArgType, "M").WithLocation(26, 27), + // (26,27): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // public async Task M() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(26, 27) + }; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular14); + comp.VerifyEmitDiagnostics(expectedDiagnostics); + + comp = CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void SpillAssignmentToThisStruct_04() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S : I + { + public int P { get; set; } + + static async Task Main() + { + S s = default; + await s.M(); + Console.Write(s.P); + } + + static Task GetInt() => Task.FromResult(1); + } + + interface I + { + public int P { get; set; } + } + + static class Extensions + { + extension(ref T t) where T : I + { + public async Task M() + { + t.P = 1; + return; + } + } + } + """; + + var expectedDiagnostics = new[] { + // (24,22): error CS9300: The 'ref' receiver parameter of an extension block must be a value type or a generic type constrained to struct. + // extension(ref T t) where T : I + Diagnostic(ErrorCode.ERR_RefExtensionParameterMustBeValueTypeOrConstrainedToOne, "T").WithLocation(24, 22), + // (26,27): error CS1988: Async methods cannot have ref, in or out parameters + // public async Task M() + Diagnostic(ErrorCode.ERR_BadAsyncArgType, "M").WithLocation(26, 27), + // (26,27): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // public async Task M() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(26, 27) + }; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular14); + comp.VerifyEmitDiagnostics(expectedDiagnostics); + + comp = CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void SpillAssignmentToThisStruct_05() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S + { + int i; + + readonly async Task M() + { + await Task.Yield(); + Console.Write(i); + return; + } + + static async Task Main() + { + S s = default; + await s.M(); + } + + static Task GetInt() => Task.FromResult(1); + } + """; + + var expectedOutput = "0"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x34 } + [Main]: Return value missing on the stack. { Offset = 0x14 } + """ + }); + + verifier.VerifyDiagnostics( + // (5,9): warning CS0649: Field 'S.i' is never assigned to, and will always have its default value 0 + // int i; + Diagnostic(ErrorCode.WRN_UnassignedInternalField, "i").WithArguments("S.i", "0").WithLocation(5, 9) + ); + verifier.VerifyIL("S.M()", """ + { + // Code size 53 (0x35) + .maxstack 2 + .locals init (System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_0, + System.Runtime.CompilerServices.YieldAwaitable V_1) + IL_0000: ldarg.0 + IL_0001: ldobj "S" + IL_0006: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_000b: stloc.1 + IL_000c: ldloca.s V_1 + IL_000e: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0013: stloc.0 + IL_0014: ldloca.s V_0 + IL_0016: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_001b: brtrue.s IL_0023 + IL_001d: ldloc.0 + IL_001e: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_0023: ldloca.s V_0 + IL_0025: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_002a: ldfld "int S.i" + IL_002f: call "void System.Console.Write(int)" + IL_0034: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToThisStruct_01() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S + { + int i; + + async Task M() + { + Console.Write(i += await GetInt()); + } + + static async Task Main() + { + S s = default; + await s.M(); + Console.Write(s.i); + } + + static Task GetInt() => Task.FromResult(1); + } + """; + + var expectedOutput = "10"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x2b } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M()", """ + { + // Code size 44 (0x2c) + .maxstack 3 + .locals init (S V_0, + int V_1, + int V_2, + int V_3) + IL_0000: ldarg.0 + IL_0001: ldobj "S" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldfld "int S.i" + IL_000d: stloc.1 + IL_000e: call "System.Threading.Tasks.Task S.GetInt()" + IL_0013: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0018: stloc.2 + IL_0019: ldloca.s V_0 + IL_001b: ldloc.1 + IL_001c: ldloc.2 + IL_001d: add + IL_001e: dup + IL_001f: stloc.3 + IL_0020: stfld "int S.i" + IL_0025: ldloc.3 + IL_0026: call "void System.Console.Write(int)" + IL_002b: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToThisStruct_02() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S + { + int i; + + public static S operator +(S s, int value) + { + s.i += value; + return s; + } + + async Task M() + { + Console.Write((this += await GetInt()).i); + } + + static async Task Main() + { + S s = default; + await s.M(); + Console.Write(s.i); + } + + static Task GetInt() => Task.FromResult(1); + } + """; + + var expectedOutput = "10"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x21 } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M()", """ + { + // Code size 34 (0x22) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldobj "S" + IL_0006: call "System.Threading.Tasks.Task S.GetInt()" + IL_000b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0010: stloc.0 + IL_0011: ldloc.0 + IL_0012: call "S S.op_Addition(S, int)" + IL_0017: ldfld "int S.i" + IL_001c: call "void System.Console.Write(int)" + IL_0021: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToThisStruct_03() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S + { + int i; + + public static S operator +(S s, int value) + { + s.i += value; + return s; + } + + async Task M() + { + this += await GetInt(); + Console.Write(i); + } + + static async Task Main() + { + S s = default; + await s.M(); + Console.Write(s.i); + } + + static Task GetInt() => Task.FromResult(1); + } + """; + + var expectedOutput = "10"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x21 } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M()", """ + { + // Code size 34 (0x22) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldobj "S" + IL_0006: call "System.Threading.Tasks.Task S.GetInt()" + IL_000b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0010: stloc.0 + IL_0011: ldloc.0 + IL_0012: call "S S.op_Addition(S, int)" + IL_0017: ldfld "int S.i" + IL_001c: call "void System.Console.Write(int)" + IL_0021: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToThisStruct_04() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S + { + int i; + + public static S operator +(S s, int value) + { + s.i += value; + return s; + } + + async Task M() + { + this += await GetInt(); + this += await GetInt(); + Console.Write(i); + } + + static async Task Main() + { + S s = default; + await s.M(); + Console.Write(s.i); + } + + static Task GetInt() => Task.FromResult(1); + } + """; + + var expectedOutput = "20"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x32 } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M()", """ + { + // Code size 51 (0x33) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldobj "S" + IL_0006: call "System.Threading.Tasks.Task S.GetInt()" + IL_000b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0010: stloc.0 + IL_0011: ldloc.0 + IL_0012: call "S S.op_Addition(S, int)" + IL_0017: call "System.Threading.Tasks.Task S.GetInt()" + IL_001c: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: stloc.0 + IL_0022: ldloc.0 + IL_0023: call "S S.op_Addition(S, int)" + IL_0028: ldfld "int S.i" + IL_002d: call "void System.Console.Write(int)" + IL_0032: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToThisStruct_05() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S + { + int i; + + public void operator +=(int value) + { + this.i += value; + } + + async Task M() + { + this += await GetInt(); + Console.Write(i); + } + + static async Task Main() + { + S s = default; + await s.M(); + Console.Write(s.i); + } + + static Task GetInt() => Task.FromResult(1); + } + """; + + var expectedOutput = "10"; + CompileAndVerify([source, CompilerFeatureRequiredAttribute], expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify([source, CompilerFeatureRequiredAttribute], expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x25 } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M()", """ + { + // Code size 38 (0x26) + .maxstack 2 + .locals init (S V_0, + int V_1) + IL_0000: ldarg.0 + IL_0001: ldobj "S" + IL_0006: stloc.0 + IL_0007: call "System.Threading.Tasks.Task S.GetInt()" + IL_000c: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0011: stloc.1 + IL_0012: ldloca.s V_0 + IL_0014: ldloc.1 + IL_0015: call "void S.op_AdditionAssignment(int)" + IL_001a: ldloc.0 + IL_001b: ldfld "int S.i" + IL_0020: call "void System.Console.Write(int)" + IL_0025: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToTypeParameter_01() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S : I + { + public int Prop { get; set; } + + static async Task M(T t) where T : I + { + Console.Write(t.Prop += await GetInt()); + } + + static async Task Main() + { + S s = default; + await M(s); + Console.Write(s.Prop); + } + + static Task GetInt() => Task.FromResult(1); + } + + interface I + { + public int Prop { get; set; } + } + """; + + var expectedOutput = "10"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x72 } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M(T)", """ + { + // Code size 115 (0x73) + .maxstack 3 + .locals init (T V_0, + T V_1, + int V_2, + int V_3, + int V_4, + T V_5) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_5 + IL_0004: initobj "T" + IL_000a: ldloc.s V_5 + IL_000c: box "T" + IL_0011: brtrue.s IL_0015 + IL_0013: ldloc.0 + IL_0014: stloc.1 + IL_0015: ldloca.s V_5 + IL_0017: initobj "T" + IL_001d: ldloc.s V_5 + IL_001f: box "T" + IL_0024: brtrue.s IL_002a + IL_0026: ldloca.s V_1 + IL_0028: br.s IL_002c + IL_002a: ldloca.s V_0 + IL_002c: constrained. "T" + IL_0032: callvirt "int I.Prop.get" + IL_0037: stloc.2 + IL_0038: call "System.Threading.Tasks.Task S.GetInt()" + IL_003d: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0042: stloc.3 + IL_0043: ldloca.s V_5 + IL_0045: initobj "T" + IL_004b: ldloc.s V_5 + IL_004d: box "T" + IL_0052: brtrue.s IL_0058 + IL_0054: ldloca.s V_1 + IL_0056: br.s IL_005a + IL_0058: ldloca.s V_0 + IL_005a: ldloc.2 + IL_005b: ldloc.3 + IL_005c: add + IL_005d: dup + IL_005e: stloc.s V_4 + IL_0060: constrained. "T" + IL_0066: callvirt "void I.Prop.set" + IL_006b: ldloc.s V_4 + IL_006d: call "void System.Console.Write(int)" + IL_0072: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToTypeParameter_02() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S : I + { + public static S operator +(S left, int value) + { + left.Prop += value; + return left; + } + + public int Prop { get; set; } + + static async Task M(T t) where T : I + { + Console.Write(t.Prop += await GetInt()); + } + + static async Task Main() + { + S s = default; + await M(s); + Console.Write(s.Prop); + } + + static Task GetInt() => Task.FromResult(1); + } + + interface I where T : I + { + public static abstract T operator +(T left, int value); + public int Prop { get; set; } + } + """; + + var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "10" : null; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net90, verify: Verification.FailsPEVerify); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net90, verify: Verification.FailsPEVerify); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x72 } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M(T)", """ + { + // Code size 115 (0x73) + .maxstack 3 + .locals init (T V_0, + T V_1, + int V_2, + int V_3, + int V_4, + T V_5) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_5 + IL_0004: initobj "T" + IL_000a: ldloc.s V_5 + IL_000c: box "T" + IL_0011: brtrue.s IL_0015 + IL_0013: ldloc.0 + IL_0014: stloc.1 + IL_0015: ldloca.s V_5 + IL_0017: initobj "T" + IL_001d: ldloc.s V_5 + IL_001f: box "T" + IL_0024: brtrue.s IL_002a + IL_0026: ldloca.s V_1 + IL_0028: br.s IL_002c + IL_002a: ldloca.s V_0 + IL_002c: constrained. "T" + IL_0032: callvirt "int I.Prop.get" + IL_0037: stloc.2 + IL_0038: call "System.Threading.Tasks.Task S.GetInt()" + IL_003d: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0042: stloc.3 + IL_0043: ldloca.s V_5 + IL_0045: initobj "T" + IL_004b: ldloc.s V_5 + IL_004d: box "T" + IL_0052: brtrue.s IL_0058 + IL_0054: ldloca.s V_1 + IL_0056: br.s IL_005a + IL_0058: ldloca.s V_0 + IL_005a: ldloc.2 + IL_005b: ldloc.3 + IL_005c: add + IL_005d: dup + IL_005e: stloc.s V_4 + IL_0060: constrained. "T" + IL_0066: callvirt "void I.Prop.set" + IL_006b: ldloc.s V_4 + IL_006d: call "void System.Console.Write(int)" + IL_0072: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToTypeParameter_03() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S : I + { + public void operator +=(int value) + { + this.Prop += value; + } + + public int Prop { get; set; } + + static async Task M(T t) where T : I + { + Console.Write(t.Prop += await GetInt()); + } + + static async Task Main() + { + S s = default; + await M(s); + Console.Write(s.Prop); + } + + static Task GetInt() => Task.FromResult(1); + } + + interface I where T : I + { + public abstract void operator +=(int value); + public int Prop { get; set; } + } + """; + + var expectedOutput = "10"; + CompileAndVerify([source, CompilerFeatureRequiredAttribute], expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify([source, CompilerFeatureRequiredAttribute], expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x72 } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M(T)", """ + { + // Code size 115 (0x73) + .maxstack 3 + .locals init (T V_0, + T V_1, + int V_2, + int V_3, + int V_4, + T V_5) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_5 + IL_0004: initobj "T" + IL_000a: ldloc.s V_5 + IL_000c: box "T" + IL_0011: brtrue.s IL_0015 + IL_0013: ldloc.0 + IL_0014: stloc.1 + IL_0015: ldloca.s V_5 + IL_0017: initobj "T" + IL_001d: ldloc.s V_5 + IL_001f: box "T" + IL_0024: brtrue.s IL_002a + IL_0026: ldloca.s V_1 + IL_0028: br.s IL_002c + IL_002a: ldloca.s V_0 + IL_002c: constrained. "T" + IL_0032: callvirt "int I.Prop.get" + IL_0037: stloc.2 + IL_0038: call "System.Threading.Tasks.Task S.GetInt()" + IL_003d: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0042: stloc.3 + IL_0043: ldloca.s V_5 + IL_0045: initobj "T" + IL_004b: ldloc.s V_5 + IL_004d: box "T" + IL_0052: brtrue.s IL_0058 + IL_0054: ldloca.s V_1 + IL_0056: br.s IL_005a + IL_0058: ldloca.s V_0 + IL_005a: ldloc.2 + IL_005b: ldloc.3 + IL_005c: add + IL_005d: dup + IL_005e: stloc.s V_4 + IL_0060: constrained. "T" + IL_0066: callvirt "void I.Prop.set" + IL_006b: ldloc.s V_4 + IL_006d: call "void System.Console.Write(int)" + IL_0072: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToTypeParameter_04() + { + var source = """ + using System; + using System.Threading.Tasks; + class S : I + { + public int Prop { get; set; } + + static async Task M(T t) where T : class, I + { + Console.Write(t.Prop += await GetInt()); + } + + static async Task Main() + { + S s = new(); + await M(s); + Console.Write(s.Prop); + } + + static Task GetInt() => Task.FromResult(1); + } + + interface I + { + public int Prop { get; set; } + } + """; + + var expectedOutput = "11"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x3a } + [Main]: Return value missing on the stack. { Offset = 0x1a } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M(T)", """ + { + // Code size 59 (0x3b) + .maxstack 3 + .locals init (T V_0, + T V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: dup + IL_0005: ldobj "T" + IL_000a: stloc.1 + IL_000b: constrained. "T" + IL_0011: callvirt "int I.Prop.get" + IL_0016: stloc.2 + IL_0017: call "System.Threading.Tasks.Task S.GetInt()" + IL_001c: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: stloc.3 + IL_0022: ldloc.1 + IL_0023: box "T" + IL_0028: ldloc.2 + IL_0029: ldloc.3 + IL_002a: add + IL_002b: dup + IL_002c: stloc.s V_4 + IL_002e: callvirt "void I.Prop.set" + IL_0033: ldloc.s V_4 + IL_0035: call "void System.Console.Write(int)" + IL_003a: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToTypeParameter_05() + { + var source = """ + using System; + using System.Threading.Tasks; + class S : I + { + public static S operator +(S left, int value) + { + left.Prop += value; + return left; + } + + public int Prop { get; set; } + + static async Task M(T t) where T : class, I + { + Console.Write(t.Prop += await GetInt()); + } + + static async Task Main() + { + S s = new(); + await M(s); + Console.Write(s.Prop); + } + + static Task GetInt() => Task.FromResult(1); + } + + interface I where T : I + { + public static abstract T operator +(T left, int value); + public int Prop { get; set; } + } + """; + + var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "11" : null; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net90, verify: Verification.FailsPEVerify); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net90, verify: Verification.FailsPEVerify); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x3a } + [Main]: Return value missing on the stack. { Offset = 0x1a } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M(T)", """ + { + // Code size 59 (0x3b) + .maxstack 3 + .locals init (T V_0, + T V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: dup + IL_0005: ldobj "T" + IL_000a: stloc.1 + IL_000b: constrained. "T" + IL_0011: callvirt "int I.Prop.get" + IL_0016: stloc.2 + IL_0017: call "System.Threading.Tasks.Task S.GetInt()" + IL_001c: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: stloc.3 + IL_0022: ldloc.1 + IL_0023: box "T" + IL_0028: ldloc.2 + IL_0029: ldloc.3 + IL_002a: add + IL_002b: dup + IL_002c: stloc.s V_4 + IL_002e: callvirt "void I.Prop.set" + IL_0033: ldloc.s V_4 + IL_0035: call "void System.Console.Write(int)" + IL_003a: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToTypeParameter_06() + { + var source = """ + using System; + using System.Threading.Tasks; + class S : I + { + public void operator +=(int value) + { + this.Prop += value; + } + + public int Prop { get; set; } + + static async Task M(T t) where T : class, I + { + Console.Write(t.Prop += await GetInt()); + } + + static async Task Main() + { + S s = new(); + await M(s); + Console.Write(s.Prop); + } + + static Task GetInt() => Task.FromResult(1); + } + + interface I where T : I + { + public abstract void operator +=(int value); + public int Prop { get; set; } + } + """; + + var expectedOutput = "11"; + CompileAndVerify([source, CompilerFeatureRequiredAttribute], expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify([source, CompilerFeatureRequiredAttribute], expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x3a } + [Main]: Return value missing on the stack. { Offset = 0x1a } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M(T)", """ + { + // Code size 59 (0x3b) + .maxstack 3 + .locals init (T V_0, + T V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: dup + IL_0005: ldobj "T" + IL_000a: stloc.1 + IL_000b: constrained. "T" + IL_0011: callvirt "int I.Prop.get" + IL_0016: stloc.2 + IL_0017: call "System.Threading.Tasks.Task S.GetInt()" + IL_001c: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: stloc.3 + IL_0022: ldloc.1 + IL_0023: box "T" + IL_0028: ldloc.2 + IL_0029: ldloc.3 + IL_002a: add + IL_002b: dup + IL_002c: stloc.s V_4 + IL_002e: callvirt "void I.Prop.set" + IL_0033: ldloc.s V_4 + IL_0035: call "void System.Console.Write(int)" + IL_003a: ret + } + """); + } + + [Fact] + public void SpillSacrificialRead() + { + var source = @" +using System; +using System.Threading.Tasks; + +class C +{ + static void F1(ref int x, int y, int z) + { + x += y + z; + } + + static int F0() + { + Console.WriteLine(-1); + return 0; + } + + static async Task F2() + { + int[] x = new int[1] { 21 }; + ClearX(); + F1(ref x[0], F0(), await Task.Factory.StartNew(() => 21)); + return x[0]; + + void ClearX() => x = null; + } + + public static void Main() + { + var t = F2(); + try + { + t.Wait(); + } + catch(Exception) + { + Console.WriteLine(0); + return; + } + + Console.WriteLine(-1); + } +}"; + var expectedOutput = "0"; + CompileAndVerify(source, expectedOutput); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F2]: Unexpected type on the stack. { Offset = 0x6d, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.F2()", """ + { + // Code size 110 (0x6e) + .maxstack 5 + .locals init (C.<>c__DisplayClass2_0 V_0, //CS$<>8__locals0 + int V_1, + int V_2) + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.1 + IL_0003: newarr "int" + IL_0008: dup + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.s 21 + IL_000c: stelem.i4 + IL_000d: stfld "int[] C.<>c__DisplayClass2_0.x" + IL_0012: ldloca.s V_0 + IL_0014: call "void C.g__ClearX|2_0(ref C.<>c__DisplayClass2_0)" + IL_0019: ldloc.0 + IL_001a: ldfld "int[] C.<>c__DisplayClass2_0.x" + IL_001f: dup + IL_0020: ldc.i4.0 + IL_0021: ldelem.i4 + IL_0022: pop + IL_0023: call "int C.F0()" + IL_0028: stloc.1 + IL_0029: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_002e: ldsfld "System.Func C.<>c.<>9__2_1" + IL_0033: dup + IL_0034: brtrue.s IL_004d + IL_0036: pop + IL_0037: ldsfld "C.<>c C.<>c.<>9" + IL_003c: ldftn "int C.<>c.b__2_1()" + IL_0042: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0047: dup + IL_0048: stsfld "System.Func C.<>c.<>9__2_1" + IL_004d: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0052: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0057: stloc.2 + IL_0058: ldc.i4.0 + IL_0059: ldelema "int" + IL_005e: ldloc.1 + IL_005f: ldloc.2 + IL_0060: call "void C.F1(ref int, int, int)" + IL_0065: ldloc.0 + IL_0066: ldfld "int[] C.<>c__DisplayClass2_0.x" + IL_006b: ldc.i4.0 + IL_006c: ldelem.i4 + IL_006d: ret + } + """); + } + + [Fact] + public void SpillRefThisStruct() + { + var source = @" +using System; +using System.Threading.Tasks; + +struct s1 +{ + public int X; + + public async void Goo1() + { + Bar(ref this, await Task.FromResult(42)); + } public void Goo2() { @@ -8542,54 +9988,52 @@ public static void Main() CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (9,23): error CS9328: Method 's1.Goo1()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // public async Task Goo1() - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "Goo1").WithArguments("s1.Goo1()").WithLocation(9, 23) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Goo1]: Return value missing on the stack. { Offset = 0x15 } - // [Goo1]: Return value missing on the stack. { Offset = 0x15 } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("s1.Goo1()", """ - // { - // // Code size 22 (0x16) - // .maxstack 3 - // .locals init (int V_0) - // IL_0000: ldc.i4.s 42 - // IL_0002: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(int)" - // IL_0007: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_000c: stloc.0 - // IL_000d: ldarg.0 - // IL_000e: ldarg.0 - // IL_000f: ldloc.0 - // IL_0010: call "void s1.Bar(ref s1, int)" - // IL_0015: ret - // } - // """); - - // verifier.VerifyIL("c1.Goo1()", """ - // { - // // Code size 22 (0x16) - // .maxstack 3 - // .locals init (int V_0) - // IL_0000: ldc.i4.s 42 - // IL_0002: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(int)" - // IL_0007: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_000c: stloc.0 - // IL_000d: ldarg.0 - // IL_000e: ldarg.0 - // IL_000f: ldloc.0 - // IL_0010: call "void c1.Bar(c1, int)" - // IL_0015: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Goo1]: Return value missing on the stack. { Offset = 0x1e } + [Goo1]: Return value missing on the stack. { Offset = 0x15 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("s1.Goo1()", """ + { + // Code size 31 (0x1f) + .maxstack 3 + .locals init (s1 V_0, + int V_1) + IL_0000: ldarg.0 + IL_0001: ldobj "s1" + IL_0006: stloc.0 + IL_0007: ldc.i4.s 42 + IL_0009: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(int)" + IL_000e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0013: stloc.1 + IL_0014: ldloca.s V_0 + IL_0016: ldloca.s V_0 + IL_0018: ldloc.1 + IL_0019: call "void s1.Bar(ref s1, int)" + IL_001e: ret + } + """); + + verifier.VerifyIL("c1.Goo1()", """ + { + // Code size 22 (0x16) + .maxstack 3 + .locals init (int V_0) + IL_0000: ldc.i4.s 42 + IL_0002: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(int)" + IL_0007: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000c: stloc.0 + IL_000d: ldarg.0 + IL_000e: ldarg.0 + IL_000f: ldloc.0 + IL_0010: call "void c1.Bar(c1, int)" + IL_0015: ret + } + """); } [Fact] @@ -8832,10 +10276,11 @@ public static void Main() } "; - var v = CompileAndVerify(source, "42"); + var expectedOutput = "42"; + var v = CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - var verifier = CompileAndVerify(comp, verify: Verification.Fails with + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with { ILVerifyMessage = """ [M]: Return value missing on the stack. { Offset = 0x12 } @@ -8892,12 +10337,16 @@ public static void Main() } "; - var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics( + var expected = // (18,28): error CS8178: A reference returned by a call to 'C.P.get' cannot be preserved across 'await' or 'yield' boundary. // Assign(second: ref P, first: await t); - Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, "P").WithArguments("C.P.get").WithLocation(18, 28) - ); + Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, "P").WithArguments("C.P.get").WithLocation(18, 28); + + var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(expected); + + comp = CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics(expected); } [Fact] @@ -10669,6 +12118,7 @@ static async Task TestAIsNull() Console.WriteLine(""Before Assignment""); try { + // Sacrificial read should ensure that `await` never happens await Assign(a); } catch (NullReferenceException) @@ -10904,6 +12354,7 @@ static async Task TestAIsNull() Console.WriteLine(""Before Assignment""); try { + // Sacrificial read should ensure that `await` never happens await Assign(a); } catch (NullReferenceException) diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs index 6f4c06140b2..8e6c9975733 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs @@ -20,6 +20,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { + [CompilerTrait(CompilerFeature.Async)] public class CodeGenAsyncTests : EmitMetadataTestBase { // https://github.com/dotnet/roslyn/issues/79792: Use the real value when possible @@ -2361,6 +2362,28 @@ public static void Main() public static int Result = -1; }"; CompileAndVerify(source, expectedOutput: "0", options: TestOptions.UnsafeDebugExe, verify: Verification.Passes); + + var comp = CreateRuntimeAsyncCompilation(source); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [getBaseMyProp]: Unexpected type on the stack. { Offset = 0x11, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("TestClass.getBaseMyProp()", """ + { + // Code size 18 (0x12) + .maxstack 1 + IL_0000: ldc.i4.1 + IL_0001: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.Delay(int)" + IL_0006: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000b: ldarg.0 + IL_000c: call "int Base.MyProp.get" + IL_0011: ret + } + """); } [Fact] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs index f5b2774dc30..21249e80d70 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { - [CompilerTrait(CompilerFeature.AsyncStreams)] + [CompilerTrait(CompilerFeature.AsyncStreams, CompilerFeature.Async)] public class CodeGenAwaitForeachTests : EmitMetadataTestBase { [Fact] @@ -4272,84 +4272,78 @@ public async ValueTask DisposeAsync() CompileAndVerify(comp, expectedOutput: expectedOutput); var runtimeAsyncComp = CreateRuntimeAsyncCompilation(source); - runtimeAsyncComp.VerifyEmitDiagnostics( - // (31,33): error CS9328: Method 'C.AsyncEnumerator.MoveNextAsync()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // public async Task MoveNextAsync() - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "MoveNextAsync").WithArguments("C.AsyncEnumerator.MoveNextAsync()").WithLocation(31, 33) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Main]: Return value missing on the stack. { Offset = 0x8c } - // [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5d, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } - // """ - // }); - // verifier.VerifyIL("C.Main()", """ - // { - // // Code size 141 (0x8d) - // .maxstack 2 - // .locals init (C.AsyncEnumerator V_0, - // object V_1, - // int V_2, //i - // System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_3) - // IL_0000: newobj "C..ctor()" - // IL_0005: call "C.AsyncEnumerator C.GetAsyncEnumerator()" - // IL_000a: stloc.0 - // IL_000b: ldnull - // IL_000c: stloc.1 - // .try - // { - // IL_000d: br.s IL_004b - // IL_000f: ldloca.s V_0 - // IL_0011: call "int C.AsyncEnumerator.Current.get" - // IL_0016: stloc.2 - // IL_0017: ldc.i4.6 - // IL_0018: ldc.i4.1 - // IL_0019: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" - // IL_001e: stloc.3 - // IL_001f: ldloca.s V_3 - // IL_0021: ldstr "Got(" - // IL_0026: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" - // IL_002b: ldloca.s V_3 - // IL_002d: ldloc.2 - // IL_002e: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" - // IL_0033: ldloca.s V_3 - // IL_0035: ldstr ") " - // IL_003a: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" - // IL_003f: ldloca.s V_3 - // IL_0041: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" - // IL_0046: call "void System.Console.Write(string)" - // IL_004b: ldloca.s V_0 - // IL_004d: call "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" - // IL_0052: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0057: brtrue.s IL_000f - // IL_0059: leave.s IL_005e - // } - // catch object - // { - // IL_005b: stloc.1 - // IL_005c: leave.s IL_005e - // } - // IL_005e: ldloca.s V_0 - // IL_0060: call "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" - // IL_0065: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" - // IL_006a: ldloc.1 - // IL_006b: brfalse.s IL_0082 - // IL_006d: ldloc.1 - // IL_006e: isinst "System.Exception" - // IL_0073: dup - // IL_0074: brtrue.s IL_0078 - // IL_0076: ldloc.1 - // IL_0077: throw - // IL_0078: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" - // IL_007d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" - // IL_0082: ldstr "Done" - // IL_0087: call "void System.Console.Write(string)" - // IL_008c: ret - // } - // """); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x8c } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x65, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 141 (0x8d) + .maxstack 2 + .locals init (C.AsyncEnumerator V_0, + object V_1, + int V_2, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_3) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.AsyncEnumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_004b + IL_000f: ldloca.s V_0 + IL_0011: call "int C.AsyncEnumerator.Current.get" + IL_0016: stloc.2 + IL_0017: ldc.i4.6 + IL_0018: ldc.i4.1 + IL_0019: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_001e: stloc.3 + IL_001f: ldloca.s V_3 + IL_0021: ldstr "Got(" + IL_0026: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_002b: ldloca.s V_3 + IL_002d: ldloc.2 + IL_002e: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_0033: ldloca.s V_3 + IL_0035: ldstr ") " + IL_003a: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_003f: ldloca.s V_3 + IL_0041: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_0046: call "void System.Console.Write(string)" + IL_004b: ldloca.s V_0 + IL_004d: call "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" + IL_0052: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0057: brtrue.s IL_000f + IL_0059: leave.s IL_005e + } + catch object + { + IL_005b: stloc.1 + IL_005c: leave.s IL_005e + } + IL_005e: ldloca.s V_0 + IL_0060: call "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_0065: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_006a: ldloc.1 + IL_006b: brfalse.s IL_0082 + IL_006d: ldloc.1 + IL_006e: isinst "System.Exception" + IL_0073: dup + IL_0074: brtrue.s IL_0078 + IL_0076: ldloc.1 + IL_0077: throw + IL_0078: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_007d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0082: ldstr "Done" + IL_0087: call "void System.Console.Write(string)" + IL_008c: ret + } + """); } [Fact] @@ -6447,92 +6441,84 @@ public async ValueTask DisposeAsync() expectedOutput: expectedOutput); var runtimeAsyncComp = CreateRuntimeAsyncCompilation(source); - runtimeAsyncComp.VerifyEmitDiagnostics( - // (32,38): error CS9328: Method 'C.AsyncEnumerator.MoveNextAsync()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // public async ValueTask MoveNextAsync() - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "MoveNextAsync").WithArguments("C.AsyncEnumerator.MoveNextAsync()").WithLocation(32, 38), - // (39,32): error CS9328: Method 'C.AsyncEnumerator.DisposeAsync()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // public async ValueTask DisposeAsync() - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "DisposeAsync").WithArguments("C.AsyncEnumerator.DisposeAsync()").WithLocation(39, 32) - ); - // var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Main]: Return value missing on the stack. { Offset = 0x96 } - // [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5d, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } - // [DisposeAsync]: Return value missing on the stack. { Offset = 0x5f } - // """ - // }); - // verifier.VerifyIL("C.Main()", """ - // { - // // Code size 151 (0x97) - // .maxstack 2 - // .locals init (System.Collections.Generic.IAsyncEnumerator V_0, - // System.Threading.CancellationToken V_1, - // object V_2, - // int V_3, //i - // System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4) - // IL_0000: newobj "C..ctor()" - // IL_0005: ldloca.s V_1 - // IL_0007: initobj "System.Threading.CancellationToken" - // IL_000d: ldloc.1 - // IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" - // IL_0013: stloc.0 - // IL_0014: ldnull - // IL_0015: stloc.2 - // .try - // { - // IL_0016: br.s IL_0054 - // IL_0018: ldloc.0 - // IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" - // IL_001e: stloc.3 - // IL_001f: ldc.i4.6 - // IL_0020: ldc.i4.1 - // IL_0021: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" - // IL_0026: stloc.s V_4 - // IL_0028: ldloca.s V_4 - // IL_002a: ldstr "Got(" - // IL_002f: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" - // IL_0034: ldloca.s V_4 - // IL_0036: ldloc.3 - // IL_0037: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" - // IL_003c: ldloca.s V_4 - // IL_003e: ldstr ") " - // IL_0043: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" - // IL_0048: ldloca.s V_4 - // IL_004a: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" - // IL_004f: call "void System.Console.Write(string)" - // IL_0054: ldloc.0 - // IL_0055: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" - // IL_005a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" - // IL_005f: brtrue.s IL_0018 - // IL_0061: leave.s IL_0066 - // } - // catch object - // { - // IL_0063: stloc.2 - // IL_0064: leave.s IL_0066 - // } - // IL_0066: ldloc.0 - // IL_0067: brfalse.s IL_0074 - // IL_0069: ldloc.0 - // IL_006a: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" - // IL_006f: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" - // IL_0074: ldloc.2 - // IL_0075: brfalse.s IL_008c - // IL_0077: ldloc.2 - // IL_0078: isinst "System.Exception" - // IL_007d: dup - // IL_007e: brtrue.s IL_0082 - // IL_0080: ldloc.2 - // IL_0081: throw - // IL_0082: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" - // IL_0087: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" - // IL_008c: ldstr "Done" - // IL_0091: call "void System.Console.Write(string)" - // IL_0096: ret - // } - // """); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x96 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x65, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x66 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 151 (0x97) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + int V_3, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0054 + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldc.i4.6 + IL_0020: ldc.i4.1 + IL_0021: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0026: stloc.s V_4 + IL_0028: ldloca.s V_4 + IL_002a: ldstr "Got(" + IL_002f: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0034: ldloca.s V_4 + IL_0036: ldloc.3 + IL_0037: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_003c: ldloca.s V_4 + IL_003e: ldstr ") " + IL_0043: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0048: ldloca.s V_4 + IL_004a: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_004f: call "void System.Console.Write(string)" + IL_0054: ldloc.0 + IL_0055: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_005a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005f: brtrue.s IL_0018 + IL_0061: leave.s IL_0066 + } + catch object + { + IL_0063: stloc.2 + IL_0064: leave.s IL_0066 + } + IL_0066: ldloc.0 + IL_0067: brfalse.s IL_0074 + IL_0069: ldloc.0 + IL_006a: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_006f: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0074: ldloc.2 + IL_0075: brfalse.s IL_008c + IL_0077: ldloc.2 + IL_0078: isinst "System.Exception" + IL_007d: dup + IL_007e: brtrue.s IL_0082 + IL_0080: ldloc.2 + IL_0081: throw + IL_0082: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0087: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_008c: ldstr "Done" + IL_0091: call "void System.Console.Write(string)" + IL_0096: ret + } + """); } [Fact, WorkItem(27651, "https://github.com/dotnet/roslyn/issues/27651")] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs index 942cc36a9d9..0ffcbdbf534 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { - [CompilerTrait(CompilerFeature.AsyncStreams)] + [CompilerTrait(CompilerFeature.AsyncStreams, CompilerFeature.Async)] public class CodeGenAwaitUsingTests : CSharpTestBase { [Fact] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefConditionalOperatorTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefConditionalOperatorTests.cs index eb8319ef8c9..681dc2e8e99 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefConditionalOperatorTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefConditionalOperatorTests.cs @@ -526,13 +526,17 @@ static async Task One() } }"; - var comp = CreateCompilationWithMscorlib461(source, options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics( + var expected = // (16,10): error CS8325: 'await' cannot be used in an expression containing a ref conditional operator // (b? ref val1: ref val2) += await One(); - Diagnostic(ErrorCode.ERR_RefConditionalAndAwait, "b? ref val1: ref val2").WithLocation(16, 10) - ); + Diagnostic(ErrorCode.ERR_RefConditionalAndAwait, "b? ref val1: ref val2").WithLocation(16, 10); + + var comp = CreateCompilationWithMscorlib461(source, options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(expected); + + comp = CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics(expected); } [Fact] @@ -569,15 +573,18 @@ static async Task One() return 1; } - }"; - var comp = CreateCompilationWithMscorlib461(source, options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics( + var expected = // (16,10): error CS8325: 'await' cannot be used in an expression containing a ref conditional operator // (b? ref val1: ref val2) = await One(); - Diagnostic(ErrorCode.ERR_RefConditionalAndAwait, "b? ref val1: ref val2").WithLocation(16, 10) - ); + Diagnostic(ErrorCode.ERR_RefConditionalAndAwait, "b? ref val1: ref val2").WithLocation(16, 10); + + var comp = CreateCompilationWithMscorlib461(source, options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(expected); + + comp = CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics(expected); } [Fact] @@ -2290,16 +2297,20 @@ static async Task GetC(C c) } "; - var comp = CreateCompilation(source); - - comp.VerifyEmitDiagnostics( + var expected = new[] { // (27,18): error CS8325: 'await' cannot be used in an expression containing a ref conditional operator // Test(ref b ? ref (await GetC(c1)).F : ref (await GetC(c2)).F, await GetC(new C())); Diagnostic(ErrorCode.ERR_RefConditionalAndAwait, "b ? ref (await GetC(c1)).F : ref (await GetC(c2)).F").WithLocation(27, 18), // (28,18): error CS8325: 'await' cannot be used in an expression containing a ref conditional operator // Test(ref b ? ref c1.F : ref c2.F, await GetC(new C())); Diagnostic(ErrorCode.ERR_RefConditionalAndAwait, "b ? ref c1.F : ref c2.F").WithLocation(28, 18) - ); + }; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(expected); + + comp = CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics(expected); } [Fact] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefLocalTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefLocalTests.cs index 989fda64284..7c0e6b10362 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefLocalTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefLocalTests.cs @@ -4781,5 +4781,75 @@ .maxstack 0 IL_0000: ret }"); } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79867")] + public void StackOverflow_01() + { + var source = +@" +public class Parent +{ + Child[] _children = new Child[100]; + + public void BrokenMethod() + { + ref var itemRef = ref _children[0]; + } +} + +public record class Child(Parent> Parent) { } +"; + CompileAndVerify(source + IsExternalInitTypeDefinition, verify: Verification.FailsPEVerify).VerifyDiagnostics(); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79867")] + public void StackOverflow_02() + { + var source = +@" +public struct Parent +{ + Child[] _children; + + public void BrokenMethod() + { + ref var itemRef = ref _children[0]; + } +} + +public record struct Child(Parent> Parent) { } +"; + CompileAndVerify(source + IsExternalInitTypeDefinition, verify: Verification.FailsPEVerify).VerifyDiagnostics(); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79867")] + public void StackOverflow_03() + { + var source = +@" +public struct Parent +{ + Child _children; + + public void BrokenMethod() + { + ref var itemRef = ref _children; + } +} + +public record struct Child(Parent> Parent) { } +"; + CreateCompilation(source + IsExternalInitTypeDefinition).VerifyEmitDiagnostics( + // (4,14): error CS0523: Struct member 'Parent._children' of type 'Child' causes a cycle in the struct layout + // Child _children; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "_children").WithArguments("Parent._children", "Child").WithLocation(4, 14), + // (12,48): error CS0523: Struct member 'Child.Parent' of type 'Parent>' causes a cycle in the struct layout + // public record struct Child(Parent> Parent) { } + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "Parent").WithArguments("Child.Parent", "Parent>").WithLocation(12, 48) + ); + } } } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenCallTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenCallTests.cs index d524096891b..336be79f4e3 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenCallTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenCallTests.cs @@ -34306,8 +34306,119 @@ static async Task Get0() } """; + var expectedOutput = "123123125:123123125:123123125"; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125").VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [Test1]: Return value missing on the stack. { Offset = 0x77 } + [Test2]: Return value missing on the stack. { Offset = 0x2b } + [Test3]: Return value missing on the stack. { Offset = 0x21 } + [Get0]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test1()", """ + { + // Code size 120 (0x78) + .maxstack 3 + .locals init (T V_0, + int V_1, + int V_2, + T V_3) + IL_0000: ldloca.s V_3 + IL_0002: initobj "T" + IL_0008: ldloc.3 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: call "System.Threading.Tasks.Task Program.Get0()" + IL_0023: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0028: stloc.1 + IL_0029: ldloca.s V_3 + IL_002b: initobj "T" + IL_0031: ldloc.3 + IL_0032: box "T" + IL_0037: brtrue.s IL_003d + IL_0039: ldloca.s V_0 + IL_003b: br.s IL_0042 + IL_003d: ldsflda "T Program.F" + IL_0042: ldloc.1 + IL_0043: constrained. "T" + IL_0049: callvirt "int I1.this[int].get" + IL_004e: ldc.i4.1 + IL_004f: add + IL_0050: stloc.2 + IL_0051: ldloca.s V_3 + IL_0053: initobj "T" + IL_0059: ldloc.3 + IL_005a: box "T" + IL_005f: brtrue.s IL_0065 + IL_0061: ldloca.s V_0 + IL_0063: br.s IL_006a + IL_0065: ldsflda "T Program.F" + IL_006a: ldloc.1 + IL_006b: ldloc.2 + IL_006c: constrained. "T" + IL_0072: callvirt "void I1.this[int].set" + IL_0077: ret + } + """); + verifier.VerifyIL("Program.Test2()", """ + { + // Code size 44 (0x2c) + .maxstack 3 + .locals init (int V_0, + int V_1) + IL_0000: ldsfld "T Program.F" + IL_0005: call "System.Threading.Tasks.Task Program.Get0()" + IL_000a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000f: stloc.0 + IL_0010: dup + IL_0011: box "T" + IL_0016: ldloc.0 + IL_0017: callvirt "int I1.this[int].get" + IL_001c: ldc.i4.1 + IL_001d: add + IL_001e: stloc.1 + IL_001f: box "T" + IL_0024: ldloc.0 + IL_0025: ldloc.1 + IL_0026: callvirt "void I1.this[int].set" + IL_002b: ret + } + """); + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 34 (0x22) + .maxstack 3 + .locals init (int V_0, + int V_1) + IL_0000: ldsfld "C1 Program.F" + IL_0005: call "System.Threading.Tasks.Task Program.Get0()" + IL_000a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000f: stloc.0 + IL_0010: dup + IL_0011: ldloc.0 + IL_0012: callvirt "int C1.this[int].get" + IL_0017: ldc.i4.1 + IL_0018: add + IL_0019: stloc.1 + IL_001a: ldloc.0 + IL_001b: ldloc.1 + IL_001c: callvirt "void C1.this[int].set" + IL_0021: ret + } + """); } [Fact] @@ -34601,8 +34712,119 @@ static async Task Get0() } """; + var expectedOutput = "123123125:123123125:123123125"; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125").VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [Test1]: Return value missing on the stack. { Offset = 0x77 } + [Test2]: Return value missing on the stack. { Offset = 0x2b } + [Test3]: Return value missing on the stack. { Offset = 0x21 } + [Get0]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test1()", """ + { + // Code size 120 (0x78) + .maxstack 4 + .locals init (T V_0, + int V_1, + int V_2, + T V_3) + IL_0000: ldloca.s V_3 + IL_0002: initobj "T" + IL_0008: ldloc.3 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: call "System.Threading.Tasks.Task Program.Get0()" + IL_0023: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0028: stloc.1 + IL_0029: ldloca.s V_3 + IL_002b: initobj "T" + IL_0031: ldloc.3 + IL_0032: box "T" + IL_0037: brtrue.s IL_003d + IL_0039: ldloca.s V_0 + IL_003b: br.s IL_0042 + IL_003d: ldsflda "T Program.F" + IL_0042: ldloc.1 + IL_0043: constrained. "T" + IL_0049: callvirt "int I1.this[int].get" + IL_004e: stloc.2 + IL_004f: ldloca.s V_3 + IL_0051: initobj "T" + IL_0057: ldloc.3 + IL_0058: box "T" + IL_005d: brtrue.s IL_0063 + IL_005f: ldloca.s V_0 + IL_0061: br.s IL_0068 + IL_0063: ldsflda "T Program.F" + IL_0068: ldloc.1 + IL_0069: ldloc.2 + IL_006a: ldc.i4.1 + IL_006b: add + IL_006c: constrained. "T" + IL_0072: callvirt "void I1.this[int].set" + IL_0077: ret + } + """); + verifier.VerifyIL("Program.Test2()", """ + { + // Code size 44 (0x2c) + .maxstack 4 + .locals init (int V_0, + int V_1) + IL_0000: ldsfld "T Program.F" + IL_0005: call "System.Threading.Tasks.Task Program.Get0()" + IL_000a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000f: stloc.0 + IL_0010: dup + IL_0011: box "T" + IL_0016: ldloc.0 + IL_0017: callvirt "int I1.this[int].get" + IL_001c: stloc.1 + IL_001d: box "T" + IL_0022: ldloc.0 + IL_0023: ldloc.1 + IL_0024: ldc.i4.1 + IL_0025: add + IL_0026: callvirt "void I1.this[int].set" + IL_002b: ret + } + """); + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 34 (0x22) + .maxstack 4 + .locals init (int V_0, + int V_1) + IL_0000: ldsfld "C1 Program.F" + IL_0005: call "System.Threading.Tasks.Task Program.Get0()" + IL_000a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000f: stloc.0 + IL_0010: dup + IL_0011: ldloc.0 + IL_0012: callvirt "int C1.this[int].get" + IL_0017: stloc.1 + IL_0018: ldloc.0 + IL_0019: ldloc.1 + IL_001a: ldc.i4.1 + IL_001b: add + IL_001c: callvirt "void C1.this[int].set" + IL_0021: ret + } + """); } [Fact] @@ -34973,8 +35195,116 @@ static async Task Get0() } """; + var expectedOutput = "123123125:123123125:123123125"; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125").VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [Test1]: Return value missing on the stack. { Offset = 0x75 } + [Test2]: Return value missing on the stack. { Offset = 0x2b } + [Test3]: Return value missing on the stack. { Offset = 0x21 } + [Get0]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test1()", """ + { + // Code size 118 (0x76) + .maxstack 4 + .locals init (T V_0, + int V_1, + T V_2) + IL_0000: ldloca.s V_2 + IL_0002: initobj "T" + IL_0008: ldloc.2 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: call "System.Threading.Tasks.Task Program.Get0()" + IL_0023: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0028: stloc.1 + IL_0029: ldloca.s V_2 + IL_002b: initobj "T" + IL_0031: ldloc.2 + IL_0032: box "T" + IL_0037: brtrue.s IL_003d + IL_0039: ldloca.s V_0 + IL_003b: br.s IL_0042 + IL_003d: ldsflda "T Program.F" + IL_0042: ldloc.1 + IL_0043: ldloca.s V_2 + IL_0045: initobj "T" + IL_004b: ldloc.2 + IL_004c: box "T" + IL_0051: brtrue.s IL_0057 + IL_0053: ldloca.s V_0 + IL_0055: br.s IL_005c + IL_0057: ldsflda "T Program.F" + IL_005c: ldloc.1 + IL_005d: constrained. "T" + IL_0063: callvirt "int I1.this[int].get" + IL_0068: ldc.i4.1 + IL_0069: add + IL_006a: constrained. "T" + IL_0070: callvirt "void I1.this[int].set" + IL_0075: ret + } + """); + verifier.VerifyIL("Program.Test2()", """ + { + // Code size 44 (0x2c) + .maxstack 4 + .locals init (T V_0, + int V_1) + IL_0000: ldsfld "T Program.F" + IL_0005: stloc.0 + IL_0006: call "System.Threading.Tasks.Task Program.Get0()" + IL_000b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0010: stloc.1 + IL_0011: ldloc.0 + IL_0012: box "T" + IL_0017: ldloc.1 + IL_0018: ldloc.0 + IL_0019: box "T" + IL_001e: ldloc.1 + IL_001f: callvirt "int I1.this[int].get" + IL_0024: ldc.i4.1 + IL_0025: add + IL_0026: callvirt "void I1.this[int].set" + IL_002b: ret + } + """); + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 34 (0x22) + .maxstack 4 + .locals init (C1 V_0, + int V_1) + IL_0000: ldsfld "C1 Program.F" + IL_0005: stloc.0 + IL_0006: call "System.Threading.Tasks.Task Program.Get0()" + IL_000b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0010: stloc.1 + IL_0011: ldloc.0 + IL_0012: ldloc.1 + IL_0013: ldloc.0 + IL_0014: ldloc.1 + IL_0015: callvirt "int C1.this[int].get" + IL_001a: ldc.i4.1 + IL_001b: add + IL_001c: callvirt "void C1.this[int].set" + IL_0021: ret + } + """); } [Fact] @@ -35057,8 +35387,129 @@ static async Task Get1() } """; + var expectedOutput = "123123125:123123125:123123125"; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125").VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [Test1]: Return value missing on the stack. { Offset = 0x77 } + [Test2]: Return value missing on the stack. { Offset = 0x36 } + [Test3]: Return value missing on the stack. { Offset = 0x23 } + [Get1]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test1()", """ + { + // Code size 120 (0x78) + .maxstack 4 + .locals init (T V_0, + int V_1, + int V_2, + T V_3) + IL_0000: ldloca.s V_3 + IL_0002: initobj "T" + IL_0008: ldloc.3 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: ldloca.s V_3 + IL_0020: initobj "T" + IL_0026: ldloc.3 + IL_0027: box "T" + IL_002c: brtrue.s IL_0032 + IL_002e: ldloca.s V_0 + IL_0030: br.s IL_0037 + IL_0032: ldsflda "T Program.F" + IL_0037: ldc.i4.0 + IL_0038: constrained. "T" + IL_003e: callvirt "int I1.this[int].get" + IL_0043: stloc.1 + IL_0044: call "System.Threading.Tasks.Task Program.Get1()" + IL_0049: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_004e: stloc.2 + IL_004f: ldloca.s V_3 + IL_0051: initobj "T" + IL_0057: ldloc.3 + IL_0058: box "T" + IL_005d: brtrue.s IL_0063 + IL_005f: ldloca.s V_0 + IL_0061: br.s IL_0068 + IL_0063: ldsflda "T Program.F" + IL_0068: ldc.i4.0 + IL_0069: ldloc.1 + IL_006a: ldloc.2 + IL_006b: add + IL_006c: constrained. "T" + IL_0072: callvirt "void I1.this[int].set" + IL_0077: ret + } + """); + verifier.VerifyIL("Program.Test2()", """ + { + // Code size 55 (0x37) + .maxstack 4 + .locals init (T V_0, + T V_1, + int V_2, + int V_3) + IL_0000: ldsfld "T Program.F" + IL_0005: stloc.0 + IL_0006: ldloca.s V_0 + IL_0008: dup + IL_0009: ldobj "T" + IL_000e: stloc.1 + IL_000f: ldc.i4.0 + IL_0010: constrained. "T" + IL_0016: callvirt "int I1.this[int].get" + IL_001b: stloc.2 + IL_001c: call "System.Threading.Tasks.Task Program.Get1()" + IL_0021: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0026: stloc.3 + IL_0027: ldloc.1 + IL_0028: box "T" + IL_002d: ldc.i4.0 + IL_002e: ldloc.2 + IL_002f: ldloc.3 + IL_0030: add + IL_0031: callvirt "void I1.this[int].set" + IL_0036: ret + } + """); + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 36 (0x24) + .maxstack 4 + .locals init (C1 V_0, + int V_1, + int V_2) + IL_0000: ldsfld "C1 Program.F" + IL_0005: dup + IL_0006: stloc.0 + IL_0007: ldc.i4.0 + IL_0008: callvirt "int C1.this[int].get" + IL_000d: stloc.1 + IL_000e: call "System.Threading.Tasks.Task Program.Get1()" + IL_0013: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0018: stloc.2 + IL_0019: ldloc.0 + IL_001a: ldc.i4.0 + IL_001b: ldloc.1 + IL_001c: ldloc.2 + IL_001d: add + IL_001e: callvirt "void C1.this[int].set" + IL_0023: ret + } + """); } [Fact] @@ -35148,8 +35599,147 @@ static async Task Get1() } """; + var expectedOutput = "123123126:123123126:123123126"; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "123123126:123123126:123123126").VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [Test1]: Return value missing on the stack. { Offset = 0x89 } + [Test2]: Return value missing on the stack. { Offset = 0x3c } + [Test3]: Return value missing on the stack. { Offset = 0x32 } + [Get0]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Get1]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test1()", """ + { + // Code size 138 (0x8a) + .maxstack 4 + .locals init (T V_0, + int V_1, + int V_2, + int V_3, + int V_4, + T V_5) + IL_0000: ldloca.s V_5 + IL_0002: initobj "T" + IL_0008: ldloc.s V_5 + IL_000a: box "T" + IL_000f: brtrue.s IL_0019 + IL_0011: ldsfld "T Program.F" + IL_0016: stloc.0 + IL_0017: br.s IL_001f + IL_0019: ldsfld "T Program.F" + IL_001e: pop + IL_001f: call "System.Threading.Tasks.Task Program.Get0()" + IL_0024: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0029: stloc.1 + IL_002a: ldloc.1 + IL_002b: stloc.2 + IL_002c: ldloca.s V_5 + IL_002e: initobj "T" + IL_0034: ldloc.s V_5 + IL_0036: box "T" + IL_003b: brtrue.s IL_0041 + IL_003d: ldloca.s V_0 + IL_003f: br.s IL_0046 + IL_0041: ldsflda "T Program.F" + IL_0046: ldloc.1 + IL_0047: constrained. "T" + IL_004d: callvirt "int I1.this[int].get" + IL_0052: stloc.3 + IL_0053: call "System.Threading.Tasks.Task Program.Get1()" + IL_0058: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_005d: stloc.s V_4 + IL_005f: ldloca.s V_5 + IL_0061: initobj "T" + IL_0067: ldloc.s V_5 + IL_0069: box "T" + IL_006e: brtrue.s IL_0074 + IL_0070: ldloca.s V_0 + IL_0072: br.s IL_0079 + IL_0074: ldsflda "T Program.F" + IL_0079: ldloc.2 + IL_007a: ldloc.3 + IL_007b: ldloc.s V_4 + IL_007d: add + IL_007e: constrained. "T" + IL_0084: callvirt "void I1.this[int].set" + IL_0089: ret + } + """); + verifier.VerifyIL("Program.Test2()", """ + { + // Code size 61 (0x3d) + .maxstack 4 + .locals init (int V_0, + T V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldsfld "T Program.F" + IL_0005: call "System.Threading.Tasks.Task Program.Get0()" + IL_000a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000f: stloc.0 + IL_0010: dup + IL_0011: stloc.1 + IL_0012: ldloc.0 + IL_0013: stloc.2 + IL_0014: box "T" + IL_0019: ldloc.0 + IL_001a: callvirt "int I1.this[int].get" + IL_001f: stloc.3 + IL_0020: call "System.Threading.Tasks.Task Program.Get1()" + IL_0025: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002a: stloc.s V_4 + IL_002c: ldloc.1 + IL_002d: box "T" + IL_0032: ldloc.2 + IL_0033: ldloc.3 + IL_0034: ldloc.s V_4 + IL_0036: add + IL_0037: callvirt "void I1.this[int].set" + IL_003c: ret + } + """); + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 51 (0x33) + .maxstack 4 + .locals init (int V_0, + C1 V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldsfld "C1 Program.F" + IL_0005: call "System.Threading.Tasks.Task Program.Get0()" + IL_000a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000f: stloc.0 + IL_0010: dup + IL_0011: stloc.1 + IL_0012: ldloc.0 + IL_0013: stloc.2 + IL_0014: ldloc.0 + IL_0015: callvirt "int C1.this[int].get" + IL_001a: stloc.3 + IL_001b: call "System.Threading.Tasks.Task Program.Get1()" + IL_0020: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: stloc.s V_4 + IL_0027: ldloc.1 + IL_0028: ldloc.2 + IL_0029: ldloc.3 + IL_002a: ldloc.s V_4 + IL_002c: add + IL_002d: callvirt "void C1.this[int].set" + IL_0032: ret + } + """); } [ConditionalFact(typeof(CoreClrOnly))] @@ -35480,8 +36070,143 @@ static async Task Get0() } """; + var expectedOutput = "123123126:123123126:123123126"; var comp = CreateCompilation(src, targetFramework: TargetFramework.NetLatest, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "123123126:123123126:123123126").VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [Test1]: Return value missing on the stack. { Offset = 0x9c } + [Test2]: Return value missing on the stack. { Offset = 0x39 } + [Test3]: Return value missing on the stack. { Offset = 0x2a } + [Get0]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test1()", """ + { + // Code size 157 (0x9d) + .maxstack 4 + .locals init (T V_0, + int V_1, + int V_2, + T V_3) + IL_0000: ldloca.s V_3 + IL_0002: initobj "T" + IL_0008: ldloc.3 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: call "System.Threading.Tasks.Task Program.Get0()" + IL_0023: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0028: stloc.1 + IL_0029: ldloca.s V_3 + IL_002b: initobj "T" + IL_0031: ldloc.3 + IL_0032: box "T" + IL_0037: brtrue.s IL_003d + IL_0039: ldloca.s V_0 + IL_003b: br.s IL_0042 + IL_003d: ldsflda "T Program.F" + IL_0042: constrained. "T" + IL_0048: callvirt "int I1.Length.get" + IL_004d: ldloc.1 + IL_004e: sub + IL_004f: stloc.2 + IL_0050: ldloca.s V_3 + IL_0052: initobj "T" + IL_0058: ldloc.3 + IL_0059: box "T" + IL_005e: brtrue.s IL_0064 + IL_0060: ldloca.s V_0 + IL_0062: br.s IL_0069 + IL_0064: ldsflda "T Program.F" + IL_0069: ldloc.2 + IL_006a: ldloca.s V_3 + IL_006c: initobj "T" + IL_0072: ldloc.3 + IL_0073: box "T" + IL_0078: brtrue.s IL_007e + IL_007a: ldloca.s V_0 + IL_007c: br.s IL_0083 + IL_007e: ldsflda "T Program.F" + IL_0083: ldloc.2 + IL_0084: constrained. "T" + IL_008a: callvirt "int I1.this[int].get" + IL_008f: ldc.i4.1 + IL_0090: add + IL_0091: constrained. "T" + IL_0097: callvirt "void I1.this[int].set" + IL_009c: ret + } + """); + verifier.VerifyIL("Program.Test2()", """ + { + // Code size 58 (0x3a) + .maxstack 4 + .locals init (T V_0, + int V_1, + int V_2) + IL_0000: ldsfld "T Program.F" + IL_0005: stloc.0 + IL_0006: call "System.Threading.Tasks.Task Program.Get0()" + IL_000b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0010: stloc.1 + IL_0011: ldloc.0 + IL_0012: box "T" + IL_0017: callvirt "int I1.Length.get" + IL_001c: ldloc.1 + IL_001d: sub + IL_001e: stloc.2 + IL_001f: ldloc.0 + IL_0020: box "T" + IL_0025: ldloc.2 + IL_0026: ldloc.0 + IL_0027: box "T" + IL_002c: ldloc.2 + IL_002d: callvirt "int I1.this[int].get" + IL_0032: ldc.i4.1 + IL_0033: add + IL_0034: callvirt "void I1.this[int].set" + IL_0039: ret + } + """); + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 43 (0x2b) + .maxstack 4 + .locals init (C1 V_0, + int V_1, + int V_2) + IL_0000: ldsfld "C1 Program.F" + IL_0005: stloc.0 + IL_0006: call "System.Threading.Tasks.Task Program.Get0()" + IL_000b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0010: stloc.1 + IL_0011: ldloc.0 + IL_0012: callvirt "int C1.Length.get" + IL_0017: ldloc.1 + IL_0018: sub + IL_0019: stloc.2 + IL_001a: ldloc.0 + IL_001b: ldloc.2 + IL_001c: ldloc.0 + IL_001d: ldloc.2 + IL_001e: callvirt "int C1.this[int].get" + IL_0023: ldc.i4.1 + IL_0024: add + IL_0025: callvirt "void C1.this[int].set" + IL_002a: ret + } + """); } [ConditionalFact(typeof(CoreClrOnly))] @@ -35574,8 +36299,155 @@ static async Task Get1() } """; + var expectedOutput = "123123126:123123126:123123126"; var comp = CreateCompilation(src, targetFramework: TargetFramework.NetLatest, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "123123126:123123126:123123126").VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [Test1]: Return value missing on the stack. { Offset = 0xa4 } + [Test2]: Return value missing on the stack. { Offset = 0x3d } + [Test3]: Return value missing on the stack. { Offset = 0x2e } + [Get1]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test1()", """ + { + // Code size 165 (0xa5) + .maxstack 4 + .locals init (T V_0, + int V_1, + int V_2, + int V_3, + int V_4, + T V_5) + IL_0000: ldloca.s V_5 + IL_0002: initobj "T" + IL_0008: ldloc.s V_5 + IL_000a: box "T" + IL_000f: brtrue.s IL_0019 + IL_0011: ldsfld "T Program.F" + IL_0016: stloc.0 + IL_0017: br.s IL_001f + IL_0019: ldsfld "T Program.F" + IL_001e: pop + IL_001f: ldloca.s V_5 + IL_0021: initobj "T" + IL_0027: ldloc.s V_5 + IL_0029: box "T" + IL_002e: brtrue.s IL_0034 + IL_0030: ldloca.s V_0 + IL_0032: br.s IL_0039 + IL_0034: ldsflda "T Program.F" + IL_0039: constrained. "T" + IL_003f: callvirt "int I1.Length.get" + IL_0044: stloc.1 + IL_0045: ldloc.1 + IL_0046: stloc.2 + IL_0047: ldloca.s V_5 + IL_0049: initobj "T" + IL_004f: ldloc.s V_5 + IL_0051: box "T" + IL_0056: brtrue.s IL_005c + IL_0058: ldloca.s V_0 + IL_005a: br.s IL_0061 + IL_005c: ldsflda "T Program.F" + IL_0061: ldloc.1 + IL_0062: constrained. "T" + IL_0068: callvirt "int I1.this[int].get" + IL_006d: stloc.3 + IL_006e: call "System.Threading.Tasks.Task Program.Get1()" + IL_0073: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0078: stloc.s V_4 + IL_007a: ldloca.s V_5 + IL_007c: initobj "T" + IL_0082: ldloc.s V_5 + IL_0084: box "T" + IL_0089: brtrue.s IL_008f + IL_008b: ldloca.s V_0 + IL_008d: br.s IL_0094 + IL_008f: ldsflda "T Program.F" + IL_0094: ldloc.2 + IL_0095: ldloc.3 + IL_0096: ldloc.s V_4 + IL_0098: add + IL_0099: constrained. "T" + IL_009f: callvirt "void I1.this[int].set" + IL_00a4: ret + } + """); + verifier.VerifyIL("Program.Test2()", """ + { + // Code size 62 (0x3e) + .maxstack 4 + .locals init (int V_0, + T V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldsfld "T Program.F" + IL_0005: dup + IL_0006: box "T" + IL_000b: callvirt "int I1.Length.get" + IL_0010: stloc.0 + IL_0011: dup + IL_0012: stloc.1 + IL_0013: ldloc.0 + IL_0014: stloc.2 + IL_0015: box "T" + IL_001a: ldloc.0 + IL_001b: callvirt "int I1.this[int].get" + IL_0020: stloc.3 + IL_0021: call "System.Threading.Tasks.Task Program.Get1()" + IL_0026: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002b: stloc.s V_4 + IL_002d: ldloc.1 + IL_002e: box "T" + IL_0033: ldloc.2 + IL_0034: ldloc.3 + IL_0035: ldloc.s V_4 + IL_0037: add + IL_0038: callvirt "void I1.this[int].set" + IL_003d: ret + } + """); + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 47 (0x2f) + .maxstack 4 + .locals init (int V_0, + C1 V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldsfld "C1 Program.F" + IL_0005: dup + IL_0006: callvirt "int C1.Length.get" + IL_000b: stloc.0 + IL_000c: dup + IL_000d: stloc.1 + IL_000e: ldloc.0 + IL_000f: stloc.2 + IL_0010: ldloc.0 + IL_0011: callvirt "int C1.this[int].get" + IL_0016: stloc.3 + IL_0017: call "System.Threading.Tasks.Task Program.Get1()" + IL_001c: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: stloc.s V_4 + IL_0023: ldloc.1 + IL_0024: ldloc.2 + IL_0025: ldloc.3 + IL_0026: ldloc.s V_4 + IL_0028: add + IL_0029: callvirt "void C1.this[int].set" + IL_002e: ret + } + """); } [ConditionalFact(typeof(CoreClrOnly))] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj b/src/roslyn/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj index a79819fbe93..a3b0da3b6e0 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj @@ -28,6 +28,7 @@ + @@ -35,4 +36,4 @@ - \ No newline at end of file + diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Diagnostics/DiagnosticAnalyzerTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Diagnostics/DiagnosticAnalyzerTests.cs index ad1e6be9648..bb6dc5aecab 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Diagnostics/DiagnosticAnalyzerTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Diagnostics/DiagnosticAnalyzerTests.cs @@ -23,6 +23,7 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; +using Roslyn.Test.Utilities.TestGenerators; using Roslyn.Utilities; using Xunit; @@ -4441,5 +4442,203 @@ record B(int I) : A(I); var diagnostics = await compWithAnalyzers.GetAnalyzerSemanticDiagnosticsAsync(model, filterSpan: null, CancellationToken.None); diagnostics.Verify(Diagnostic("ID0001", "B").WithLocation(1, 8)); } + + private sealed class OptionsOverrideDiagnosticAnalyzer(AnalyzerConfigOptionsProvider customOptions) : DiagnosticAnalyzer + { + private static readonly DiagnosticDescriptor s_descriptor = new DiagnosticDescriptor( + id: "ID0001", + title: "Title", + messageFormat: "Message", + category: "Category", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + private readonly AnalyzerConfigOptionsProvider _customOptions = customOptions; + + public override ImmutableArray SupportedDiagnostics => [s_descriptor]; + + public bool RegisterAdditionalFileActionInvoked { get; private set; } + public bool RegisterCodeBlockActionInvoked { get; private set; } + public bool RegisterCodeBlockStartActionInvoked { get; private set; } + public bool RegisterCompilationActionInvoked { get; private set; } + public bool RegisterOperationActionInvoked { get; private set; } + public bool RegisterOperationBlockActionInvoked { get; private set; } + public bool RegisterSemanticModelActionInvoked { get; private set; } + public bool RegisterSymbolActionInvoked { get; private set; } + public bool RegisterSyntaxNodeActionInvoked { get; private set; } + public bool RegisterSyntaxTreeActionInvoked { get; private set; } + + public bool RegisterOperationBlockStartActionInvoked { get; private set; } + public bool RegisterOperationBlockEndActionInvoked { get; private set; } + public bool RegisterCompilationStartActionInvoked { get; private set; } + public bool RegisterCompilationEndActionInvoked { get; private set; } + public bool RegisterSymbolStartActionInvoked { get; private set; } + public bool RegisterSymbolEndActionInvoked { get; private set; } + + public AnalyzerOptions SeenOptions; + + private void AssertSame(AnalyzerOptions options) + { + // First, assert that the options provider we see is the custom one the test sets. + Assert.Same(options.AnalyzerConfigOptionsProvider, _customOptions); + + if (SeenOptions is null) + SeenOptions = options; + + // Also ensure that the compiler actually passes the same AnalyzerOptions wrapper around + // the options provider. That ensures we're not accidentally creating new instances unnecessarily. + Assert.Same(SeenOptions, options); + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterAdditionalFileAction(context => { AssertSame(context.Options); RegisterAdditionalFileActionInvoked = true; }); + context.RegisterCodeBlockAction(context => { AssertSame(context.Options); RegisterCodeBlockActionInvoked = true; }); + context.RegisterCodeBlockStartAction(context => { AssertSame(context.Options); RegisterCodeBlockStartActionInvoked = true; }); + context.RegisterCompilationAction(context => { AssertSame(context.Options); RegisterCompilationActionInvoked = true; }); + context.RegisterOperationAction(context => { AssertSame(context.Options); RegisterOperationActionInvoked = true; }, OperationKind.Block); + context.RegisterOperationBlockAction(context => { AssertSame(context.Options); RegisterOperationBlockActionInvoked = true; }); + context.RegisterSemanticModelAction(context => { AssertSame(context.Options); RegisterSemanticModelActionInvoked = true; }); + context.RegisterSymbolAction(context => { AssertSame(context.Options); RegisterSymbolActionInvoked = true; }, SymbolKind.NamedType); + context.RegisterSyntaxNodeAction(context => { AssertSame(context.Options); RegisterSyntaxNodeActionInvoked = true; }, SyntaxKind.ClassDeclaration); + context.RegisterSyntaxTreeAction(context => { AssertSame(context.Options); RegisterSyntaxTreeActionInvoked = true; }); + + context.RegisterOperationBlockStartAction(context => + { + AssertSame(context.Options); + RegisterOperationBlockStartActionInvoked = true; + context.RegisterOperationBlockEndAction(context => + { + AssertSame(context.Options); + RegisterOperationBlockEndActionInvoked = true; + }); + }); + + context.RegisterCompilationStartAction(context => + { + AssertSame(context.Options); + RegisterCompilationStartActionInvoked = true; + context.RegisterCompilationEndAction(context => + { + AssertSame(context.Options); + RegisterCompilationEndActionInvoked = true; + }); + }); + context.RegisterSymbolStartAction(context => + { + AssertSame(context.Options); + RegisterSymbolStartActionInvoked = true; + context.RegisterSymbolEndAction(context => + { + AssertSame(context.Options); + RegisterSymbolEndActionInvoked = true; + }); + }, SymbolKind.NamedType); + } + + public void AssertAllCallbacksInvoked() + { + Assert.NotNull(SeenOptions); + + Assert.True(RegisterAdditionalFileActionInvoked); + + Assert.True(RegisterAdditionalFileActionInvoked); + Assert.True(RegisterCodeBlockActionInvoked); + Assert.True(RegisterCodeBlockStartActionInvoked); + Assert.True(RegisterCompilationActionInvoked); + Assert.True(RegisterOperationActionInvoked); + Assert.True(RegisterOperationBlockActionInvoked); + Assert.True(RegisterSemanticModelActionInvoked); + Assert.True(RegisterSymbolActionInvoked); + Assert.True(RegisterSyntaxNodeActionInvoked); + Assert.True(RegisterSyntaxTreeActionInvoked); + + Assert.True(RegisterOperationBlockStartActionInvoked); + Assert.True(RegisterOperationBlockEndActionInvoked); + Assert.True(RegisterCompilationStartActionInvoked); + Assert.True(RegisterCompilationEndActionInvoked); + Assert.True(RegisterSymbolStartActionInvoked); + Assert.True(RegisterSymbolEndActionInvoked); + } + } + + [Fact] + public async Task TestAnalyzerSpecificOptionsFactory() + { + // lang=C#-Test + string source = """ + class C + { + void M() + { + int x = 0; + } + } + """; + + var tree = CSharpSyntaxTree.ParseText(source); + var compilation = CreateCompilationWithCSharp(new[] { tree, CSharpSyntaxTree.ParseText(IsExternalInitTypeDefinition) }); + compilation.VerifyDiagnostics( + // (5,13): warning CS0219: The variable 'x' is assigned but its value is never used + // int x = 0; + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(5, 13)); + + var additionalText = new InMemoryAdditionalText("path", "content"); + + // Ensure that the analyzer only sees the custom options passed to the callbacks, and never the shared options. + var sharedOptions = new AnalyzerOptions([additionalText]); + + // Test1. Just a single analyzer. Ensure all callbacks get the custom options. + { + var customOptions = new CompilerAnalyzerConfigOptionsProvider( + ImmutableDictionary.Empty, + new DictionaryAnalyzerConfigOptions( + ImmutableDictionary.Empty)); + Assert.NotSame(sharedOptions, customOptions); + + var analyzer = new OptionsOverrideDiagnosticAnalyzer(customOptions); + + var compWithAnalyzers = new CompilationWithAnalyzers( + compilation, + [analyzer], + new CompilationWithAnalyzersOptions( + sharedOptions, onAnalyzerException: null, concurrentAnalysis: false, logAnalyzerExecutionTime: false, reportSuppressedDiagnostics: false, analyzerExceptionFilter: null, + _ => customOptions)); + + var diagnostics = await compWithAnalyzers.GetAllDiagnosticsAsync(); + Assert.Single(diagnostics); + + analyzer.AssertAllCallbacksInvoked(); + } + + // Test2. Two analyzers. Ensure both gets the custom options across all callbacks. + // Also, ensure that across the analyzers we're getting the exact same AnalyzerOptions instance. + { + var customOptions = new CompilerAnalyzerConfigOptionsProvider( + ImmutableDictionary.Empty, + new DictionaryAnalyzerConfigOptions( + ImmutableDictionary.Empty)); + Assert.NotSame(sharedOptions, customOptions); + + var analyzer1 = new OptionsOverrideDiagnosticAnalyzer(customOptions); + var analyzer2 = new OptionsOverrideDiagnosticAnalyzer(customOptions); + + var compWithAnalyzers = new CompilationWithAnalyzers( + compilation, + [analyzer1, analyzer2], + new CompilationWithAnalyzersOptions( + sharedOptions, onAnalyzerException: null, concurrentAnalysis: false, logAnalyzerExecutionTime: false, reportSuppressedDiagnostics: false, analyzerExceptionFilter: null, + _ => customOptions)); + + var diagnostics = await compWithAnalyzers.GetAllDiagnosticsAsync(); + Assert.Single(diagnostics); + + analyzer1.AssertAllCallbacksInvoked(); + analyzer2.AssertAllCallbacksInvoked(); + + // Both analyzers should get the exact same AnalyzerOptions instance since they used the same customOptions. + Assert.Same(analyzer1.SeenOptions, analyzer2.SeenOptions); + } + } } } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs index 148ea07159a..cbd54a63f5f 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs @@ -593,6 +593,130 @@ class C Diagnostic(ErrorCode.ERR_StaticAnonymousFunctionCannotCaptureThis, "field").WithLocation(4, 39)); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78932")] + public void Lambda_03() + { + var source = """ + #nullable enable + using System.Collections.Generic; + using System; + + public class DemoCscBreaks + { + public List WillBreak => MethodReturningLambda(() => field ?? new List()); + + private T MethodReturningLambda(Func thisGet) => thisGet(); + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var fieldExpression = tree.GetRoot().DescendantNodes().OfType().Single(); + var fieldType = model.GetTypeInfo(fieldExpression); + AssertEx.Equal("System.Collections.Generic.List?", fieldType.Type.ToTestDisplayString(includeNonNullable: true)); + Assert.Equal(CodeAnalysis.NullableAnnotation.Annotated, fieldType.Type.NullableAnnotation); + + // Note that the member symbol does not expose the inferred nullable annotation via 'FieldSymbol.TypeWithAnnotations'. + var fieldSymbol = comp.GetMember("DemoCscBreaks.k__BackingField"); + Assert.Equal(NullableAnnotation.NotAnnotated, fieldSymbol.TypeWithAnnotations.NullableAnnotation); + Assert.Equal(NullableAnnotation.Annotated, ((SynthesizedBackingFieldSymbol)fieldSymbol).GetInferredNullableAnnotation()); + + var publicFieldSymbol = fieldSymbol.GetPublicSymbol(); + Assert.Equal(CodeAnalysis.NullableAnnotation.NotAnnotated, publicFieldSymbol.NullableAnnotation); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78932")] + public void Lambda_04() + { + var source = """ + #nullable enable + using System.Collections.Generic; + using System; + + public class Program + { + public List Prop + { + get => M(() => field); + set => M(() => field = value ?? new List()); + } + + private T M(Func fn) => fn(); + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (7,25): warning CS9264: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or safely handling the case where 'field' is null in the 'get' accessor. + // public List Prop + Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(7, 25)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var fieldExpressions = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(2, fieldExpressions.Length); + verify(fieldExpressions[0]); + // https://github.com/dotnet/roslyn/issues/77215: a setter should have a maybe-null initial state for the 'field'. + verify(fieldExpressions[1]); + + void verify(FieldExpressionSyntax fieldExpression) + { + var fieldType = model.GetTypeInfo(fieldExpression); + AssertEx.Equal("System.Collections.Generic.List!", fieldType.Type.ToTestDisplayString(includeNonNullable: true)); + Assert.Equal(CodeAnalysis.NullableAnnotation.NotAnnotated, fieldType.Type.NullableAnnotation); + + var fieldSymbol = comp.GetMember("Program.k__BackingField"); + Assert.Equal(NullableAnnotation.NotAnnotated, fieldSymbol.TypeWithAnnotations.NullableAnnotation); + Assert.Equal(NullableAnnotation.NotAnnotated, ((SynthesizedBackingFieldSymbol)fieldSymbol).GetInferredNullableAnnotation()); + + var publicFieldSymbol = fieldSymbol.GetPublicSymbol(); + Assert.Equal(CodeAnalysis.NullableAnnotation.NotAnnotated, publicFieldSymbol.NullableAnnotation); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78932")] + public void Lambda_05() + { + var source = """ + #nullable enable + using System.Collections.Generic; + using System; + + public class Program + { + public List Prop => M(() => (List)[field[0].ToString()]); + + private T M(Func fn) => fn(); + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (7,26): warning CS9264: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or safely handling the case where 'field' is null in the 'get' accessor. + // public List Prop => M(() => (List)[field[0].ToString()]); + Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(7, 26), + // (7,58): warning CS8602: Dereference of a possibly null reference. + // public List Prop => M(() => (List)[field[0].ToString()]); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field[0]").WithLocation(7, 58)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var fieldExpression = tree.GetRoot().DescendantNodes().OfType().Single(); + var fieldType = model.GetTypeInfo(fieldExpression); + AssertEx.Equal("System.Collections.Generic.List!", fieldType.Type.ToTestDisplayString(includeNonNullable: true)); + Assert.Equal(CodeAnalysis.NullableAnnotation.NotAnnotated, fieldType.Type.NullableAnnotation); + + var fieldSymbol = comp.GetMember("Program.k__BackingField"); + Assert.Equal(NullableAnnotation.NotAnnotated, fieldSymbol.TypeWithAnnotations.NullableAnnotation); + Assert.Equal(NullableAnnotation.NotAnnotated, ((SynthesizedBackingFieldSymbol)fieldSymbol).GetInferredNullableAnnotation()); + + var publicFieldSymbol = fieldSymbol.GetPublicSymbol(); + Assert.Equal(CodeAnalysis.NullableAnnotation.NotAnnotated, publicFieldSymbol.NullableAnnotation); + } + [Fact] public void LocalFunction_01() { diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Microsoft.CodeAnalysis.CSharp.Emit3.UnitTests.csproj b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Microsoft.CodeAnalysis.CSharp.Emit3.UnitTests.csproj index 801f88a7e61..be883e4c060 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Microsoft.CodeAnalysis.CSharp.Emit3.UnitTests.csproj +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Microsoft.CodeAnalysis.CSharp.Emit3.UnitTests.csproj @@ -29,6 +29,7 @@ + diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs index 3edeaabec13..efa0af3603f 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs @@ -11994,6 +11994,404 @@ static void Main() Verification.Skipped).VerifyDiagnostics(); } + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/80041")] + public void Foreach_Span_01() + { + var src = @" +class C +{ + static void Test(System.ReadOnlySpan collection) + { + foreach(var c in collection) + { + } + } +} +"; + + var core = +@" +namespace System +{ + public class Object { } + public abstract class ValueType { } + public class String { } + public class Type { } + public struct Void { } + public struct Boolean { } + public struct Int32 { } + public struct Char { } + public struct Enum { } + public class Attribute { } + public class AttributeUsageAttribute : Attribute + { + public AttributeUsageAttribute(AttributeTargets t) { } + public bool AllowMultiple { get; set; } + public bool Inherited { get; set; } + } + public enum AttributeTargets { } + + public interface IDisposable + { + void Dispose(); + } +} +namespace System.Collections +{ + public interface IEnumerator + { + bool MoveNext(); + object Current { get; } + } + public interface IEnumerable + { + IEnumerator GetEnumerator(); + } +} +namespace System.Collections.Generic +{ + public interface IEnumerator : IDisposable, IEnumerator + { + new T Current { get; } + } + public interface IEnumerable : IEnumerable + { + new IEnumerator GetEnumerator(); + } +} + +namespace System.Runtime.InteropServices +{ + public class InAttribute {} +} + +namespace System.Reflection +{ + public class DefaultMemberAttribute : Attribute + { + public DefaultMemberAttribute(string name) { } + } +} +"; + + var expectedIL = +@" +{ + // Code size 30 (0x1e) + .maxstack 2 + .locals init (System.ReadOnlySpan V_0, + int V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldc.i4.0 + IL_0003: stloc.1 + IL_0004: br.s IL_0013 + IL_0006: ldloca.s V_0 + IL_0008: ldloc.1 + IL_0009: call ""ref readonly char System.ReadOnlySpan.this[int].get"" + IL_000e: pop + IL_000f: ldloc.1 + IL_0010: ldc.i4.1 + IL_0011: add + IL_0012: stloc.1 + IL_0013: ldloc.1 + IL_0014: ldloca.s V_0 + IL_0016: call ""int System.ReadOnlySpan.Length.get"" + IL_001b: blt.s IL_0006 + IL_001d: ret +} +"; + + var span1 = @" +namespace System +{ + public readonly ref struct ReadOnlySpan + { + private static T _t; + public int Length => 0; + public ref readonly T this[int index] => ref _t; + + public Enumerator GetEnumerator() => default; + + public ref struct Enumerator + { + public ref readonly T Current => ref _t; + + public bool MoveNext() => false; + } + } +} +"; + var span = CreateEmptyCompilation([core, span1]).EmitToImageReference(); + + var comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular11); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + var span2 = @" +namespace System +{ + public readonly ref struct ReadOnlySpan + { + private static T _t; + public int Length => 0; + public ref readonly T this[int index] => ref _t; + + public Enumerator GetEnumerator() => default; + + public ref struct Enumerator : System.Collections.Generic.IEnumerator + { + public ref readonly T Current => ref _t; + + public bool MoveNext() => false; + + T System.Collections.Generic.IEnumerator.Current => default; + object System.Collections.IEnumerator.Current => default; + void System.IDisposable.Dispose() { } + } + } +} +"; + span = CreateEmptyCompilation([core, span2]).EmitToImageReference(); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular11); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + var span3 = @" +namespace System +{ + public readonly ref struct ReadOnlySpan : System.Collections.Generic.IEnumerable + { + private static T _t; + public int Length => 0; + public ref readonly T this[int index] => ref _t; + + System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => default; + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => default; + } +} +"; + span = CreateEmptyCompilation([core, span3]).EmitToImageReference(); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular11); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.ReleaseDll); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular11); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/80041")] + public void Foreach_Span_02() + { + var src = @" +class C +{ + static void Test(System.Span collection) + { + foreach(var c in collection) + { + } + } +} +"; + + var core = +@" +namespace System +{ + public class Object { } + public abstract class ValueType { } + public class String { } + public class Type { } + public struct Void { } + public struct Boolean { } + public struct Int32 { } + public struct Char { } + public struct Enum { } + public class Attribute { } + public class AttributeUsageAttribute : Attribute + { + public AttributeUsageAttribute(AttributeTargets t) { } + public bool AllowMultiple { get; set; } + public bool Inherited { get; set; } + } + public enum AttributeTargets { } + + public interface IDisposable + { + void Dispose(); + } +} +namespace System.Collections +{ + public interface IEnumerator + { + bool MoveNext(); + object Current { get; } + } + public interface IEnumerable + { + IEnumerator GetEnumerator(); + } +} +namespace System.Collections.Generic +{ + public interface IEnumerator : IDisposable, IEnumerator + { + new T Current { get; } + } + public interface IEnumerable : IEnumerable + { + new IEnumerator GetEnumerator(); + } +} + +namespace System.Runtime.InteropServices +{ + public class InAttribute {} +} + +namespace System.Reflection +{ + public class DefaultMemberAttribute : Attribute + { + public DefaultMemberAttribute(string name) { } + } +} +"; + + var expectedIL = + @" +{ + // Code size 30 (0x1e) + .maxstack 2 + .locals init (System.Span V_0, + int V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldc.i4.0 + IL_0003: stloc.1 + IL_0004: br.s IL_0013 + IL_0006: ldloca.s V_0 + IL_0008: ldloc.1 + IL_0009: call ""ref char System.Span.this[int].get"" + IL_000e: pop + IL_000f: ldloc.1 + IL_0010: ldc.i4.1 + IL_0011: add + IL_0012: stloc.1 + IL_0013: ldloc.1 + IL_0014: ldloca.s V_0 + IL_0016: call ""int System.Span.Length.get"" + IL_001b: blt.s IL_0006 + IL_001d: ret +} +"; + + var span1 = @" +namespace System +{ + public readonly ref struct Span + { + private static T _t; + public int Length => 0; + public ref T this[int index] => ref _t; + + public Enumerator GetEnumerator() => default; + + public ref struct Enumerator + { + public ref T Current => ref _t; + + public bool MoveNext() => false; + } + } +} +"; + var span = CreateEmptyCompilation([core, span1]).EmitToImageReference(); + + var comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular11); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + var span2 = @" +namespace System +{ + public readonly ref struct Span + { + private static T _t; + public int Length => 0; + public ref T this[int index] => ref _t; + + public Enumerator GetEnumerator() => default; + + public ref struct Enumerator : System.Collections.Generic.IEnumerator + { + public ref readonly T Current => ref _t; + + public bool MoveNext() => false; + + T System.Collections.Generic.IEnumerator.Current => default; + object System.Collections.IEnumerator.Current => default; + void System.IDisposable.Dispose() { } + } + } +} +"; + span = CreateEmptyCompilation([core, span2]).EmitToImageReference(); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular11); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + var span3 = @" +namespace System +{ + public readonly ref struct Span : System.Collections.Generic.IEnumerable + { + private static T _t; + public int Length => 0; + public ref T this[int index] => ref _t; + + System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => default; + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => default; + } +} +"; + span = CreateEmptyCompilation([core, span3]).EmitToImageReference(); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular11); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.ReleaseDll); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular11); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + } + [Fact] public void AwaitUsing_01() { diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index eb94b3c5095..6c3d0b3334d 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -76,6 +76,102 @@ static class E Diagnostic(ErrorCode.ERR_MissingDeconstruct, @"""""").WithArguments("string", "2").WithLocation(1, 14)); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80217")] + public void Deconstruct_Nested_01() + { + var source = """ + var (a, b, c) = 42; + var ((d, _, _), (_, e, _), (_, _, f)) = 42; + System.Console.WriteLine($"{a} {b} {c} {d} {e} {f}"); + + static class E + { + extension(int instance) + { + public void Deconstruct(out int a, out int b, out int c) + => (a, b, c) = (1, 2, 3); + } + } + """; + CompileAndVerify(source, expectedOutput: "1 2 3 1 2 3").VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80217")] + public void Deconstruct_Nested_02() + { + var source = """ + ((int a, int b, int c), (int d, int e), (int f, int g, int h, int i)) = 42; + + static class E + { + extension(int instance) + { + public void Deconstruct(out int a, out int b, out int c) + => (a, b, c) = (1, 2, 3); + } + } + """; + CreateCompilation(source).VerifyEmitDiagnostics( + // (1,73): error CS7036: There is no argument given that corresponds to the required parameter 'c' of 'E.extension(int).Deconstruct(out int, out int, out int)' + // ((int a, int b, int c), (int d, int e), (int f, int g, int h, int i)) = 42; + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "42").WithArguments("c", "E.extension(int).Deconstruct(out int, out int, out int)").WithLocation(1, 73), + // (1,73): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'int', with 2 out parameters and a void return type. + // ((int a, int b, int c), (int d, int e), (int f, int g, int h, int i)) = 42; + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "42").WithArguments("int", "2").WithLocation(1, 73), + // (1,73): error CS1501: No overload for method 'Deconstruct' takes 4 arguments + // ((int a, int b, int c), (int d, int e), (int f, int g, int h, int i)) = 42; + Diagnostic(ErrorCode.ERR_BadArgCount, "42").WithArguments("Deconstruct", "4").WithLocation(1, 73), + // (1,73): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'int', with 4 out parameters and a void return type. + // ((int a, int b, int c), (int d, int e), (int f, int g, int h, int i)) = 42; + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "42").WithArguments("int", "4").WithLocation(1, 73)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80217")] + public void Deconstruct_Nested_03() + { + var source = """ + ((int d, int _, int _), (int _, int e, int _), (int _, int _, int f), (int _, int _, int _)) = 42; + + static class E + { + extension(int instance) + { + public void Deconstruct(out int a, out int b, out int c) + => (a, b, c) = (1, 2, 3); + } + } + """; + CreateCompilation(source).VerifyEmitDiagnostics( + // (1,96): error CS1501: No overload for method 'Deconstruct' takes 4 arguments + // ((int d, int _, int _), (int _, int e, int _), (int _, int _, int f), (int _, int _, int _)) = 42; + Diagnostic(ErrorCode.ERR_BadArgCount, "42").WithArguments("Deconstruct", "4").WithLocation(1, 96), + // (1,96): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'int', with 4 out parameters and a void return type. + // ((int d, int _, int _), (int _, int e, int _), (int _, int _, int f), (int _, int _, int _)) = 42; + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "42").WithArguments("int", "4").WithLocation(1, 96)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80217")] + public void Deconstruct_Nested_04() + { + var source = """ + var ((a, b, c), (d, e), (f, g)) = 42; + System.Console.WriteLine($"{a} {b} {c} {d} {e} {f} {g}"); + + static class E + { + extension(int instance) + { + public void Deconstruct(out int a, out int b, out int c) + => (a, b, c) = (1, 2, 3); + + public void Deconstruct(out int a, out int b) + => (a, b) = (4, 5); + } + } + """; + CompileAndVerify(source, expectedOutput: "1 2 3 4 5 4 5").VerifyDiagnostics(); + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75484")] public void Deconstruction_UnscopedRef_ExtensionMethod() { @@ -619,6 +715,73 @@ public static class E CompileAndVerify(comp, expectedOutput: "method 42").VerifyDiagnostics(); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78968")] + public void AnonymousType_03() + { + var src = """ +42.M(); + +public static class E +{ + extension(int i) + { + public void M() + { + var x = new { A = 1 }; + local(x, x => new { x.A }); + + void local(U u, System.Linq.Expressions.Expression> f) + { + System.Console.Write(f.Compile()(u)); + } + } + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); + CompileAndVerify(comp, expectedOutput: ExpectedOutput("{ A = 1 }"), verify: Verification.Skipped).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78968")] + public void AnonymousType_04() + { + var src = """ +42.M(43); + +public static class E +{ + extension(int i) + { + public void M(T t) + { + var x = new { A = t }; + local(x, x => new { x.A }); + + static void local(U u, System.Linq.Expressions.Expression> f) + { + System.Console.Write(f.Compile()(u)); + } + } + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); + CompileAndVerify(comp, expectedOutput: ExpectedOutput("{ A = 43 }"), verify: Verification.Skipped).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80250")] + public void Cls() + { + var src = """ + [assembly: System.CLSCompliant(true)] + public static class Extensions + { + extension(object) { } + } + """; + CreateCompilation(src).VerifyEmitDiagnostics(); + } + [Fact] public void Attribute_01() { @@ -6510,10 +6673,10 @@ public static void M() { } AssertEx.SequenceEqual(["(E.extension(int).@M, void E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.M())"], PrintXmlCrefSymbols(tree, model)); } - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78967")] public void Cref_52() { - // unqualified reference + // unqualified extension block var src = """ /// /// @@ -6535,50 +6698,25 @@ public static void Method() { } { } - /// + /// /// public static void M2() { } } """; - // Tracked by https://github.com/dotnet/roslyn/issues/78967 : cref, such unqualified references in CREF should work within context of enclosing static type var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); - comp.VerifyEmitDiagnostics( - // (1,16): warning CS1574: XML comment has cref attribute 'extension(int).Method' that could not be resolved - // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Method").WithArguments("extension(int).Method").WithLocation(1, 16), - // (2,16): warning CS1574: XML comment has cref attribute 'extension(int).Property' that could not be resolved - // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Property").WithArguments("extension(int).Property").WithLocation(2, 16), - // (7,24): warning CS1574: XML comment has cref attribute 'extension(int).Method' that could not be resolved - // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Method").WithArguments("extension(int).Method").WithLocation(7, 24), - // (8,24): warning CS1574: XML comment has cref attribute 'extension(int).Property' that could not be resolved - // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Property").WithArguments("extension(int).Property").WithLocation(8, 24), - // (15,20): warning CS1574: XML comment has cref attribute 'extension(int).Method' that could not be resolved - // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Method").WithArguments("extension(int).Method").WithLocation(15, 20), - // (16,20): warning CS1574: XML comment has cref attribute 'extension(int).Property' that could not be resolved - // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Property").WithArguments("extension(int).Property").WithLocation(16, 20), - // (21,20): warning CS1574: XML comment has cref attribute 'extension(int).M2' that could not be resolved - // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).M2").WithArguments("extension(int).M2").WithLocation(21, 20), - // (22,20): warning CS1574: XML comment has cref attribute 'extension(int).Property' that could not be resolved - // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Property").WithArguments("extension(int).Property").WithLocation(22, 20)); + comp.VerifyEmitDiagnostics(); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); AssertEx.Equal([ - "(extension(int).Method, null)", - "(extension(int).Property, null)", - "(extension(int).Method, null)", - "(extension(int).Property, null)", - "(extension(int).Method, null)", - "(extension(int).Property, null)", - "(extension(int).M2, null)", - "(extension(int).Property, null)"], + "(extension(int).Method, void E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.Method())", + "(extension(int).Property, System.Int32 E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.Property { get; })", + "(extension(int).Method, void E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.Method())", + "(extension(int).Property, System.Int32 E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.Property { get; })", + "(extension(int).Method, void E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.Method())", + "(extension(int).Property, System.Int32 E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.Property { get; })", + "(extension(int).Method, void E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.Method())", + "(extension(int).Property, System.Int32 E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.Property { get; })"], PrintXmlCrefSymbols(tree, model)); src = """ @@ -6694,6 +6832,331 @@ static class E Diagnostic(ErrorCode.WRN_BadXMLRef, "E.M(string)").WithArguments("M(string)").WithLocation(1, 16)); } + [Fact] + public void Cref_56() + { + var src = """ +/// +static class E +{ + extension(ref int i) + { + public static void M() => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(int).M()' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int).M()").WithArguments("extension(int).M()").WithLocation(1, 16)); + + var src2 = """ +/// +static class E +{ + extension(int i) + { + public static void M() => throw null!; + } +} +"""; + var comp2 = CreateCompilation(src2, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp2.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(ref int).M()' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(ref int).M()").WithArguments("extension(ref int).M()").WithLocation(1, 16)); + + var extension = comp.GetMember("E").GetTypeMembers().Single(); + var extension2 = comp2.GetMember("E").GetTypeMembers().Single(); + Assert.True(extension.ExtensionGroupingName == extension2.ExtensionGroupingName); + } + + [Fact] + public void Cref_57() + { + var src = """ +/// +static class E +{ + extension(int) + { + public static void M() => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(int)' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int)").WithArguments("extension(int)").WithLocation(1, 16)); + } + + [Fact] + public void Cref_58() + { + var src = """ +static class E +{ + extension(int) + { + /// + public static void M() => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal([ + "(extension(int).M(), void E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.M())"], + PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_59() + { + var src = """ +static class E +{ + /// + extension(T) + { + /// + public static void M() => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal([ + "(extension{U}(U).M(), void E.$8048A6C8BE30A622530249B904B537EB.M())", + "(extension{V}(V).M(), void E.$8048A6C8BE30A622530249B904B537EB.M())"], + PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_60() + { + var src = """ +static class E +{ + /// + extension(int) + { + /// + public static void M(T t) => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal([ + "(extension{U}(int).M(U), void E.$B8D310208B4544F25EEBACB9990FC73B.M(U t))", + "(extension{V}(int).M(V), void E.$B8D310208B4544F25EEBACB9990FC73B.M(V t))"], + PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_61() + { + // in namespace + var src = """ +namespace E +{ + extension(int) + { + /// + public static void M(T t) => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (3,5): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(int) + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(3, 5), + // (3,5): warning CS1591: Missing XML comment for publicly visible type or member 'extension(int)' + // extension(int) + Diagnostic(ErrorCode.WRN_MissingXMLComment, "extension").WithArguments("E.extension(int)").WithLocation(3, 5), + // (5,24): warning CS1574: XML comment has cref attribute 'extension{U}(int).M(U)' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension{U}(int).M(U)").WithArguments("extension{U}(int).M(U)").WithLocation(5, 24)); + } + + [Fact] + public void Cref_62() + { + // top-level extension block + var src = """ +/// +extension(int) +{ + /// + public static void M(T t) => throw null!; +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension{U}(int).M(U)' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension{U}(int).M(U)").WithArguments("extension{U}(int).M(U)").WithLocation(1, 16), + // (2,1): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(int) + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(2, 1), + // (4,20): warning CS1574: XML comment has cref attribute 'extension{U}(int).M(U)' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension{U}(int).M(U)").WithArguments("extension{U}(int).M(U)").WithLocation(4, 20)); + } + + [Fact] + public void Cref_63() + { + var src = """ +static class E +{ + extension(object) + { + public static void M1() => throw null!; + + extension(int) + { + /// + /// + public static void M2(T t) => throw null!; + } + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (7,9): error CS9282: This member is not allowed in an extension block + // extension(int) + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "extension").WithLocation(7, 9), + // (9,28): warning CS1574: XML comment has cref attribute 'extension{U}(int).M(U)' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension{U}(int).M(U)").WithArguments("extension{U}(int).M(U)").WithLocation(9, 28), + // (10,28): warning CS1574: XML comment has cref attribute 'extension(object).M1()' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(object).M1()").WithArguments("extension(object).M1()").WithLocation(10, 28)); + } + + [Fact] + public void Cref_64() + { + // generic static enclosing class + var src = """ +static class E +{ + /// + extension(int) + { + /// + public static void M(T t) => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (4,5): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(int) + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(4, 5)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal([ + "(extension{U}(int).M(U), void E.$B8D310208B4544F25EEBACB9990FC73B.M(U t))", + "(extension{U}(int).M(U), void E.$B8D310208B4544F25EEBACB9990FC73B.M(U t))"], + PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_65() + { + // non-static enclosing class + var src = """ +class E +{ + /// + extension(int) + { + /// + public static void M(T t) => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (4,5): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(int) + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(4, 5)); + } + + [Fact] + public void Cref_66() + { + var src = """ +static class E +{ + /// + extension(int) + { + /// + public static void M(T t) => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal([ + "(extension(int).M{U}(U), void E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.M(U t))", + "(extension(int).M{U}(U), void E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.M(U t))"], + PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_67() + { + var src = """ +static class E +{ + extension(int) + { + public static void M() => throw null!; + + /// + class Nested + { + /// + public static void Method() => throw null!; + } + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (7,24): warning CS1574: XML comment has cref attribute 'extension(int).M()' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).M()").WithArguments("extension(int).M()").WithLocation(7, 24), + // (8,15): error CS9282: This member is not allowed in an extension block + // class Nested + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Nested").WithLocation(8, 15), + // (10,28): warning CS1574: XML comment has cref attribute 'extension(int).M()' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).M()").WithArguments("extension(int).M()").WithLocation(10, 28)); + } + [Fact] public void PropertyAccess_Set_01() { @@ -7884,6 +8347,58 @@ .locals init (T V_0, IL_0035: ret } "); + + comp = CreateRuntimeAsyncCompilation(src, options: TestOptions.UnsafeReleaseExe.WithSpecificDiagnosticOptions("SYSLIB5007", ReportDiagnostic.Suppress)); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Cannot change initonly field outside its .ctor. { Offset = 0xa } + [Main]: Cannot change initonly field outside its .ctor. { Offset = 0x32 } + [Main]: Return value missing on the stack. { Offset = 0x41 } + [Initialize]: Cannot change initonly field outside its .ctor. { Offset = 0x0 } + [Initialize]: Expected numeric type on the stack. { Offset = 0xc, Found = address of Int32 } + [Increment]: Cannot change initonly field outside its .ctor. { Offset = 0x0 } + [Increment]: Expected numeric type on the stack. { Offset = 0xc, Found = address of Int32 } + [Test1]: Cannot change initonly field outside its .ctor. { Offset = 0x0 } + [Test3]: Return value missing on the stack. { Offset = 0x47 } + [Get1Async]: Unexpected type on the stack. { Offset = 0x2a, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 72 (0x48) + .maxstack 2 + .locals init (T V_0, + int V_1, + T V_2) + IL_0000: ldloca.s V_2 + IL_0002: initobj "T" + IL_0008: ldloc.2 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0023: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0028: stloc.1 + IL_0029: ldloca.s V_2 + IL_002b: initobj "T" + IL_0031: ldloc.2 + IL_0032: box "T" + IL_0037: brtrue.s IL_003c + IL_0039: ldloc.0 + IL_003a: br.s IL_0041 + IL_003c: ldsfld "T Program.F" + IL_0041: ldloc.1 + IL_0042: call "void E.set_P1(T, int)" + IL_0047: ret + } + """); } [Theory] @@ -8568,6 +9083,64 @@ .locals init (int V_0) IL_0020: ret } "); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xa4 } + [Test3]: Return value missing on the stack. { Offset = 0x67 } + [Get1Async]: Unexpected type on the stack. { Offset = 0x34, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 104 (0x68) + .maxstack 2 + .locals init (T V_0, + int V_1, + int V_2, + T V_3) + IL_0000: ldloca.s V_3 + IL_0002: initobj "T" + IL_0008: ldloc.3 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: ldloca.s V_3 + IL_0020: initobj "T" + IL_0026: ldloc.3 + IL_0027: box "T" + IL_002c: brtrue.s IL_0031 + IL_002e: ldloc.0 + IL_002f: br.s IL_0036 + IL_0031: ldsfld "T Program.F" + IL_0036: call "int E.get_P1(T)" + IL_003b: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0040: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0045: stloc.1 + IL_0046: ldloc.1 + IL_0047: add + IL_0048: stloc.2 + IL_0049: ldloca.s V_3 + IL_004b: initobj "T" + IL_0051: ldloc.3 + IL_0052: box "T" + IL_0057: brtrue.s IL_005c + IL_0059: ldloc.0 + IL_005a: br.s IL_0061 + IL_005c: ldsfld "T Program.F" + IL_0061: ldloc.2 + IL_0062: call "void E.set_P1(T, int)" + IL_0067: ret + } + """); } [Fact] @@ -8666,6 +9239,38 @@ .maxstack 3 } "); + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x6a } + [Test3]: Return value missing on the stack. { Offset = 0x23 } + [Get1Async]: Unexpected type on the stack. { Offset = 0x34, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 36 (0x24) + .maxstack 3 + .locals init (int V_0, + int V_1) + IL_0000: ldsflda "T Program.F" + IL_0005: call "int E.get_P1(ref T)" + IL_000a: stloc.0 + IL_000b: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0010: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0015: stloc.1 + IL_0016: ldsflda "T Program.F" + IL_001b: ldloc.0 + IL_001c: ldloc.1 + IL_001d: add + IL_001e: call "void E.set_P1(ref T, int)" + IL_0023: ret + } + """); + var src2 = """ static class E { @@ -8862,6 +9467,64 @@ .maxstack 3 IL_0019: ret } "); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [Test3]: Return value missing on the stack. { Offset = 0x67 } + [Get1Async]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 104 (0x68) + .maxstack 2 + .locals init (T V_0, + int V_1, + int V_2, + T V_3) + IL_0000: ldloca.s V_3 + IL_0002: initobj "T" + IL_0008: ldloc.3 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: ldloca.s V_3 + IL_0020: initobj "T" + IL_0026: ldloc.3 + IL_0027: box "T" + IL_002c: brtrue.s IL_0031 + IL_002e: ldloc.0 + IL_002f: br.s IL_0036 + IL_0031: ldsfld "T Program.F" + IL_0036: call "int E.get_P1(T)" + IL_003b: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0040: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0045: stloc.1 + IL_0046: ldloc.1 + IL_0047: add + IL_0048: stloc.2 + IL_0049: ldloca.s V_3 + IL_004b: initobj "T" + IL_0051: ldloc.3 + IL_0052: box "T" + IL_0057: brtrue.s IL_005c + IL_0059: ldloc.0 + IL_005a: br.s IL_0061 + IL_005c: ldsfld "T Program.F" + IL_0061: ldloc.2 + IL_0062: call "void E.set_P1(T, int)" + IL_0067: ret + } + """); } [Fact] @@ -8956,8 +9619,9 @@ static async Task Get1Async() } """; + var expectedOutput = "123125125:123125125"; var comp = CreateCompilation(src, options: TestOptions.DebugExe.WithAllowUnsafe(true)); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125", verify: Verification.Skipped).VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1()", @" @@ -8995,6 +9659,71 @@ .locals init (T V_0, IL_0041: ret } "); + + comp = CreateRuntimeAsyncCompilation(src, options: TestOptions.UnsafeReleaseExe.WithSpecificDiagnosticOptions("SYSLIB5007", ReportDiagnostic.Suppress)); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Cannot change initonly field outside its .ctor. { Offset = 0xa } + [Main]: Cannot change initonly field outside its .ctor. { Offset = 0x32 } + [Main]: Return value missing on the stack. { Offset = 0x41 } + [Initialize]: Cannot change initonly field outside its .ctor. { Offset = 0x0 } + [Initialize]: Expected numeric type on the stack. { Offset = 0xc, Found = address of Int32 } + [Increment]: Cannot change initonly field outside its .ctor. { Offset = 0x0 } + [Increment]: Expected numeric type on the stack. { Offset = 0xc, Found = address of Int32 } + [Test1]: Cannot change initonly field outside its .ctor. { Offset = 0x0 } + [Test3]: Return value missing on the stack. { Offset = 0x67 } + [Get1Async]: Unexpected type on the stack. { Offset = 0x2a, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 104 (0x68) + .maxstack 2 + .locals init (T V_0, + int V_1, + int V_2, + T V_3) + IL_0000: ldloca.s V_3 + IL_0002: initobj "T" + IL_0008: ldloc.3 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: ldloca.s V_3 + IL_0020: initobj "T" + IL_0026: ldloc.3 + IL_0027: box "T" + IL_002c: brtrue.s IL_0031 + IL_002e: ldloc.0 + IL_002f: br.s IL_0036 + IL_0031: ldsfld "T Program.F" + IL_0036: call "int E.get_P1(T)" + IL_003b: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0040: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0045: stloc.1 + IL_0046: ldloc.1 + IL_0047: add + IL_0048: stloc.2 + IL_0049: ldloca.s V_3 + IL_004b: initobj "T" + IL_0051: ldloc.3 + IL_0052: box "T" + IL_0057: brtrue.s IL_005c + IL_0059: ldloc.0 + IL_005a: br.s IL_0061 + IL_005c: ldsfld "T Program.F" + IL_0061: ldloc.2 + IL_0062: call "void E.set_P1(T, int)" + IL_0067: ret + } + """); } [Theory] @@ -11629,6 +12358,121 @@ .locals init (T& V_0, IL_003d: ret } "); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x152 } + [Test13]: Return value missing on the stack. { Offset = 0x6d } + [Test23]: Return value missing on the stack. { Offset = 0x7f } + [Get1Async]: Unexpected type on the stack. { Offset = 0x34, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test13()", """ + { + // Code size 110 (0x6e) + .maxstack 3 + .locals init (T V_0, + T V_1, + object V_2, + object V_3) + IL_0000: ldloca.s V_1 + IL_0002: initobj "T" + IL_0008: ldloc.1 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: ldloca.s V_1 + IL_0020: initobj "T" + IL_0026: ldloc.1 + IL_0027: box "T" + IL_002c: brtrue.s IL_0031 + IL_002e: ldloc.0 + IL_002f: br.s IL_0036 + IL_0031: ldsfld "T Program.F" + IL_0036: call "object E.get_P1(T)" + IL_003b: brtrue.s IL_006d + IL_003d: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0042: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0047: box "int" + IL_004c: stloc.2 + IL_004d: ldloca.s V_1 + IL_004f: initobj "T" + IL_0055: ldloc.1 + IL_0056: box "T" + IL_005b: brtrue.s IL_0060 + IL_005d: ldloc.0 + IL_005e: br.s IL_0065 + IL_0060: ldsfld "T Program.F" + IL_0065: ldloc.2 + IL_0066: dup + IL_0067: stloc.3 + IL_0068: call "void E.set_P1(T, object)" + IL_006d: ret + } + """); + + verifier.VerifyIL("Program.Test23()", """ + { + // Code size 128 (0x80) + .maxstack 3 + .locals init (T V_0, + int? V_1, + int V_2, + T V_3, + int? V_4) + IL_0000: ldloca.s V_3 + IL_0002: initobj "T" + IL_0008: ldloc.3 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: ldloca.s V_3 + IL_0020: initobj "T" + IL_0026: ldloc.3 + IL_0027: box "T" + IL_002c: brtrue.s IL_0031 + IL_002e: ldloc.0 + IL_002f: br.s IL_0036 + IL_0031: ldsfld "T Program.F" + IL_0036: call "int? E.get_P2(T)" + IL_003b: stloc.1 + IL_003c: ldloca.s V_1 + IL_003e: call "readonly int int?.GetValueOrDefault()" + IL_0043: stloc.2 + IL_0044: ldloca.s V_1 + IL_0046: call "readonly bool int?.HasValue.get" + IL_004b: brtrue.s IL_007f + IL_004d: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0052: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0057: stloc.2 + IL_0058: ldloca.s V_3 + IL_005a: initobj "T" + IL_0060: ldloc.3 + IL_0061: box "T" + IL_0066: brtrue.s IL_006b + IL_0068: ldloc.0 + IL_0069: br.s IL_0070 + IL_006b: ldsfld "T Program.F" + IL_0070: ldloca.s V_4 + IL_0072: ldloc.2 + IL_0073: call "int?..ctor(int)" + IL_0078: ldloc.s V_4 + IL_007a: call "void E.set_P2(T, int?)" + IL_007f: ret + } + """); } [Fact] @@ -11804,6 +12648,69 @@ .locals init (T& V_0, } "); + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xde } + [Test13]: Return value missing on the stack. { Offset = 0x29 } + [Test23]: Return value missing on the stack. { Offset = 0x3a } + [Get1Async]: Unexpected type on the stack. { Offset = 0x34, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test13()", """ + { + // Code size 42 (0x2a) + .maxstack 3 + .locals init (int V_0, + object V_1) + IL_0000: ldsflda "T Program.F" + IL_0005: call "object E.get_P1(ref T)" + IL_000a: brtrue.s IL_0029 + IL_000c: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0011: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0016: stloc.0 + IL_0017: ldsflda "T Program.F" + IL_001c: ldloc.0 + IL_001d: box "int" + IL_0022: dup + IL_0023: stloc.1 + IL_0024: call "void E.set_P1(ref T, object)" + IL_0029: ret + } + """); + + verifier.VerifyIL("Program.Test23()", """ + { + // Code size 59 (0x3b) + .maxstack 3 + .locals init (int? V_0, + int V_1, + int? V_2) + IL_0000: ldsflda "T Program.F" + IL_0005: call "int? E.get_P2(ref T)" + IL_000a: stloc.0 + IL_000b: ldloca.s V_0 + IL_000d: call "readonly int int?.GetValueOrDefault()" + IL_0012: stloc.1 + IL_0013: ldloca.s V_0 + IL_0015: call "readonly bool int?.HasValue.get" + IL_001a: brtrue.s IL_003a + IL_001c: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0021: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0026: stloc.1 + IL_0027: ldsflda "T Program.F" + IL_002c: ldloca.s V_2 + IL_002e: ldloc.1 + IL_002f: call "int?..ctor(int)" + IL_0034: ldloc.2 + IL_0035: call "void E.set_P2(ref T, int?)" + IL_003a: ret + } + """); + var src2 = """ static class E { @@ -12156,6 +13063,121 @@ .locals init (T V_0, IL_0038: ret } "); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x134 } + [Test13]: Return value missing on the stack. { Offset = 0x6d } + [Test23]: Return value missing on the stack. { Offset = 0x7f } + [Get1Async]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test13()", """ + { + // Code size 110 (0x6e) + .maxstack 3 + .locals init (T V_0, + T V_1, + object V_2, + object V_3) + IL_0000: ldloca.s V_1 + IL_0002: initobj "T" + IL_0008: ldloc.1 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: ldloca.s V_1 + IL_0020: initobj "T" + IL_0026: ldloc.1 + IL_0027: box "T" + IL_002c: brtrue.s IL_0031 + IL_002e: ldloc.0 + IL_002f: br.s IL_0036 + IL_0031: ldsfld "T Program.F" + IL_0036: call "object E.get_P1(T)" + IL_003b: brtrue.s IL_006d + IL_003d: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0042: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0047: box "int" + IL_004c: stloc.2 + IL_004d: ldloca.s V_1 + IL_004f: initobj "T" + IL_0055: ldloc.1 + IL_0056: box "T" + IL_005b: brtrue.s IL_0060 + IL_005d: ldloc.0 + IL_005e: br.s IL_0065 + IL_0060: ldsfld "T Program.F" + IL_0065: ldloc.2 + IL_0066: dup + IL_0067: stloc.3 + IL_0068: call "void E.set_P1(T, object)" + IL_006d: ret + } + """); + + verifier.VerifyIL("Program.Test23()", """ + { + // Code size 128 (0x80) + .maxstack 3 + .locals init (T V_0, + int? V_1, + int V_2, + T V_3, + int? V_4) + IL_0000: ldloca.s V_3 + IL_0002: initobj "T" + IL_0008: ldloc.3 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: ldloca.s V_3 + IL_0020: initobj "T" + IL_0026: ldloc.3 + IL_0027: box "T" + IL_002c: brtrue.s IL_0031 + IL_002e: ldloc.0 + IL_002f: br.s IL_0036 + IL_0031: ldsfld "T Program.F" + IL_0036: call "int? E.get_P2(T)" + IL_003b: stloc.1 + IL_003c: ldloca.s V_1 + IL_003e: call "readonly int int?.GetValueOrDefault()" + IL_0043: stloc.2 + IL_0044: ldloca.s V_1 + IL_0046: call "readonly bool int?.HasValue.get" + IL_004b: brtrue.s IL_007f + IL_004d: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0052: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0057: stloc.2 + IL_0058: ldloca.s V_3 + IL_005a: initobj "T" + IL_0060: ldloc.3 + IL_0061: box "T" + IL_0066: brtrue.s IL_006b + IL_0068: ldloc.0 + IL_0069: br.s IL_0070 + IL_006b: ldsfld "T Program.F" + IL_0070: ldloca.s V_4 + IL_0072: ldloc.2 + IL_0073: call "int?..ctor(int)" + IL_0078: ldloc.s V_4 + IL_007a: call "void E.set_P2(T, int?)" + IL_007f: ret + } + """); } [Fact] @@ -12628,6 +13650,51 @@ .locals init (int V_0) IL_0014: ret } "); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xa4 } + [Test3]: Return value missing on the stack. { Offset = 0x47 } + [Get1Async]: Unexpected type on the stack. { Offset = 0x43, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 72 (0x48) + .maxstack 2 + .locals init (T V_0, + int V_1, + T V_2) + IL_0000: ldloca.s V_2 + IL_0002: initobj "T" + IL_0008: ldloc.2 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0023: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0028: stloc.1 + IL_0029: ldloca.s V_2 + IL_002b: initobj "T" + IL_0031: ldloc.2 + IL_0032: box "T" + IL_0037: brtrue.s IL_003c + IL_0039: ldloc.0 + IL_003a: br.s IL_0041 + IL_003c: ldsfld "T Program.F" + IL_0041: ldloc.1 + IL_0042: call "void E.set_P1(T, int)" + IL_0047: ret + } + """); } [Fact] @@ -12726,6 +13793,32 @@ .locals init (int V_0) } "); + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x6a } + [Test3]: Return value missing on the stack. { Offset = 0x16 } + [Get1Async]: Unexpected type on the stack. { Offset = 0x43, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 23 (0x17) + .maxstack 2 + .locals init (int V_0) + IL_0000: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0005: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000a: stloc.0 + IL_000b: ldsflda "T Program.F" + IL_0010: ldloc.0 + IL_0011: call "void E.set_P1(ref T, int)" + IL_0016: ret + } + """); + var src2 = """ static class E { @@ -12918,6 +14011,51 @@ .locals init (int V_0) IL_0014: ret } "); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [Test3]: Return value missing on the stack. { Offset = 0x47 } + [Get1Async]: Unexpected type on the stack. { Offset = 0x50, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 72 (0x48) + .maxstack 2 + .locals init (T V_0, + int V_1, + T V_2) + IL_0000: ldloca.s V_2 + IL_0002: initobj "T" + IL_0008: ldloc.2 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0023: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0028: stloc.1 + IL_0029: ldloca.s V_2 + IL_002b: initobj "T" + IL_0031: ldloc.2 + IL_0032: box "T" + IL_0037: brtrue.s IL_003c + IL_0039: ldloc.0 + IL_003a: br.s IL_0041 + IL_003c: ldsfld "T Program.F" + IL_0041: ldloc.1 + IL_0042: call "void E.set_P1(T, int)" + IL_0047: ret + } + """); } [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] @@ -15229,16 +16367,13 @@ struct S1 class Program { -// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Test1(); - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //System.Console.Write(":"); + System.Console.Write(":"); - //await Test3(); + await Test3(); } static T GetT() => (T)(object)new S1 { F1 = 123 }; @@ -15253,22 +16388,21 @@ static int Get1() return 1; } - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //static async Task Test3() - //{ - // GetT()[Get1(), $""] += await Get1Async(); - //} + static async Task Test3() + { + GetT()[Get1(), $""] += await Get1Async(); + } - //static async Task Get1Async() - //{ - // await Task.Yield(); - // return 1; - //} + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } } """; var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123123").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1()", @" @@ -15353,8 +16487,6 @@ class C1 class Program { -// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Test1(); @@ -15363,10 +16495,9 @@ static async Task Main() Test2(); - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //System.Console.Write(":"); + System.Console.Write(":"); - //await Test3(); + await Test3(); } static T GetT() => (T)(object)new C1 { F1 = 123 }; @@ -15386,17 +16517,16 @@ static int Get1() return 1; } - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //static async Task Test3() - //{ - // GetT()[Get1(), $""] += await Get1Async(); - //} + static async Task Test3() + { + GetT()[Get1(), $""] += await Get1Async(); + } - //static async Task Get1Async() - //{ - // await Task.Yield(); - // return 1; - //} + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } } """; @@ -31155,5 +32285,152 @@ .maxstack 8 } // end of class E """.Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); } + + [Fact] + public void AssociatedExtensionImplementation_01() + { + var src = """ +public static class E +{ + extension(int i) + { + public void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + var extensionMethod = comp.GlobalNamespace.GetTypeMember("E").GetTypeMember("").GetMember("M").GetPublicSymbol(); + Assert.Equal("void E.M(this System.Int32 i)", extensionMethod.AssociatedExtensionImplementation.ToTestDisplayString()); + } + + [Fact] + public void AssociatedExtensionImplementation_02() + { + // not a definition + var src = """ +42.M(); + +public static class E +{ + extension(T t) + { + public void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "42.M"); + var method = (IMethodSymbol)model.GetSymbolInfo(memberAccess).Symbol; + // Tracked by https://github.com/dotnet/roslyn/issues/78957 : public API, add support for constructed symbols + Assert.Null(method.AssociatedExtensionImplementation); + Assert.Equal("void E.M(this T t)", method.OriginalDefinition.AssociatedExtensionImplementation.ToTestDisplayString()); + } + + [Fact] + public void AssociatedExtensionImplementation_03() + { + // not an extension method + var src = """ +public class E +{ + public void M() { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + var method = comp.GlobalNamespace.GetTypeMember("E").GetMember("M").GetPublicSymbol(); + Assert.Null(method.AssociatedExtensionImplementation); + } + + [Fact] + public void AssociatedExtensionImplementation_04() + { + // missing implementation method in metadata + var ilSrc = """ +.class public auto ansi abstract sealed beforefieldinit E + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + .class nested public auto ansi sealed specialname '$BA41CFE2B5EDAEB8C1B9062F59ED4D69' + extends [mscorlib]System.Object + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + .class nested public auto ansi abstract sealed specialname '$BA41CFE2B5EDAEB8C1B9062F59ED4D69' + extends [mscorlib]System.Object + { + .method public hidebysig specialname static void '$' ( int32 '' ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + ret + } + } + .method public hidebysig static void M () cil managed + { + .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( + 01 00 24 3c 4d 3e 24 42 41 34 31 43 46 45 32 42 + 35 45 44 41 45 42 38 43 31 42 39 30 36 32 46 35 + 39 45 44 34 44 36 39 00 00 + ) + ldnull + throw + } + } +} +""" + ExtensionMarkerAttributeIL; + + var comp = CreateCompilationWithIL("", ilSrc); + var method = comp.GlobalNamespace.GetTypeMember("E").GetTypeMembers().Single().GetMember("M").GetPublicSymbol(); + Assert.Null(method.AssociatedExtensionImplementation); + } + + [Fact] + public void AssociatedExtensionImplementation_05() + { + // incorrect accessibility on implementation method in metadata + var ilSrc = """ +.class public auto ansi abstract sealed beforefieldinit E + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + .class nested public auto ansi sealed specialname '$BA41CFE2B5EDAEB8C1B9062F59ED4D69' + extends [mscorlib]System.Object + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + .class nested public auto ansi abstract sealed specialname '$BA41CFE2B5EDAEB8C1B9062F59ED4D69' + extends [mscorlib]System.Object + { + .method public hidebysig specialname static void '$' ( int32 '' ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + ret + } + } + .method public hidebysig static void M () cil managed + { + .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( + 01 00 24 3c 4d 3e 24 42 41 34 31 43 46 45 32 42 + 35 45 44 41 45 42 38 43 31 42 39 30 36 32 46 35 + 39 45 44 34 44 36 39 00 00 + ) + ldnull + throw + } + } + .method family hidebysig static void M () cil managed + { + ret + } +} +""" + ExtensionMarkerAttributeIL; + + var comp = CreateCompilationWithIL("", ilSrc); + var method = comp.GlobalNamespace.GetTypeMember("E").GetTypeMembers().Single().GetMember("M").GetPublicSymbol(); + Assert.Null(method.AssociatedExtensionImplementation); + } } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/InlineArrayTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/InlineArrayTests.cs index 8420d2ce0fd..b2e69217442 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/InlineArrayTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/InlineArrayTests.cs @@ -19491,8 +19491,73 @@ public T Current } } "; + var expectedOutput = " 111 112 113 114"; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: " 111 112 113 114").VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.FailsILVerify with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x7f } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Main()", """ + { + // Code size 128 (0x80) + .maxstack 2 + .locals init (C V_0, //x + Buffer4.AsyncEnumerator V_1, + System.Threading.CancellationToken V_2) + IL_0000: ldloca.s V_0 + IL_0002: initobj "C" + IL_0008: ldloca.s V_0 + IL_000a: ldflda "Buffer4 C.F" + IL_000f: call "ref int .InlineArrayFirstElementRef, int>(ref Buffer4)" + IL_0014: ldc.i4.s 111 + IL_0016: stind.i4 + IL_0017: ldloca.s V_0 + IL_0019: ldflda "Buffer4 C.F" + IL_001e: ldc.i4.1 + IL_001f: call "ref int .InlineArrayElementRef, int>(ref Buffer4, int)" + IL_0024: ldc.i4.s 112 + IL_0026: stind.i4 + IL_0027: ldloca.s V_0 + IL_0029: ldflda "Buffer4 C.F" + IL_002e: ldc.i4.2 + IL_002f: call "ref int .InlineArrayElementRef, int>(ref Buffer4, int)" + IL_0034: ldc.i4.s 113 + IL_0036: stind.i4 + IL_0037: ldloca.s V_0 + IL_0039: ldflda "Buffer4 C.F" + IL_003e: ldc.i4.3 + IL_003f: call "ref int .InlineArrayElementRef, int>(ref Buffer4, int)" + IL_0044: ldc.i4.s 114 + IL_0046: stind.i4 + IL_0047: ldloca.s V_0 + IL_0049: ldflda "Buffer4 C.F" + IL_004e: ldloca.s V_2 + IL_0050: initobj "System.Threading.CancellationToken" + IL_0056: ldloc.2 + IL_0057: call "Buffer4.AsyncEnumerator Buffer4.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_005c: stloc.1 + IL_005d: br.s IL_0071 + IL_005f: ldloc.1 + IL_0060: callvirt "int Buffer4.AsyncEnumerator.Current.get" + IL_0065: ldc.i4.s 32 + IL_0067: call "void System.Console.Write(char)" + IL_006c: call "void System.Console.Write(int)" + IL_0071: ldloc.1 + IL_0072: ldc.i4.1 + IL_0073: callvirt "System.Threading.Tasks.Task Buffer4.AsyncEnumerator.MoveNextAsync(int)" + IL_0078: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_007d: brtrue.s IL_005f + IL_007f: ret + } + """); } [Fact] @@ -20171,8 +20236,9 @@ static void Increment() } } "; + var expectedOutput = " 0 1 2 3"; var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - var verifier = CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyDiagnostics(); verifier.VerifyIL("Program.d__3.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" @@ -20323,7 +20389,67 @@ .locals init (int V_0, } "); comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyDiagnostics(); + + comp = CreateRuntimeAsyncCompilation(src + Buffer4Definition); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Test]: Return value missing on the stack. { Offset = 0x62 } + [InlineArrayAsSpan]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0xc } + """ + }); + verifier.VerifyIL("Program.Test(C)", """ + { + // Code size 99 (0x63) + .maxstack 2 + .locals init (C V_0, + int V_1, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_2, + System.Runtime.CompilerServices.YieldAwaitable V_3) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldfld "Buffer4 C.F" + IL_0008: pop + IL_0009: ldc.i4.0 + IL_000a: stloc.1 + IL_000b: br.s IL_005e + IL_000d: ldloc.0 + IL_000e: ldflda "Buffer4 C.F" + IL_0013: ldloc.1 + IL_0014: call "ref int .InlineArrayElementRef, int>(ref Buffer4, int)" + IL_0019: ldind.i4 + IL_001a: call "void Program.Increment()" + IL_001f: ldc.i4.s 32 + IL_0021: call "void System.Console.Write(char)" + IL_0026: call "void System.Console.Write(int)" + IL_002b: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0030: stloc.3 + IL_0031: ldloca.s V_3 + IL_0033: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0038: stloc.2 + IL_0039: ldloca.s V_2 + IL_003b: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0040: brtrue.s IL_0048 + IL_0042: ldloc.2 + IL_0043: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_0048: ldloca.s V_2 + IL_004a: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_004f: ldc.i4.2 + IL_0050: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.Delay(int)" + IL_0055: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_005a: ldloc.1 + IL_005b: ldc.i4.1 + IL_005c: add + IL_005d: stloc.1 + IL_005e: ldloc.1 + IL_005f: ldc.i4.4 + IL_0060: blt.s IL_000d + IL_0062: ret + } + """); } [ConditionalFact(typeof(CoreClrOnly))] @@ -20384,17 +20510,20 @@ static async void Test() static ref Buffer4 GetBuffer() => throw null; } "; - var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); - comp.VerifyEmitDiagnostics( - // (6,9): error CS8178: A reference returned by a call to 'Program.GetBuffer()' cannot be preserved across 'await' or 'yield' boundary. - // foreach (int y in GetBuffer()) - Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, + // (6,9): error CS8178: A reference returned by a call to 'Program.GetBuffer()' cannot be preserved across 'await' or 'yield' boundary. + // foreach (int y in GetBuffer()) + DiagnosticDescription expected = Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, @"foreach (int y in GetBuffer()) { await System.Threading.Tasks.Task.Yield(); - }").WithArguments("Program.GetBuffer()").WithLocation(6, 9) - ); + }").WithArguments("Program.GetBuffer()").WithLocation(6, 9); + + var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); + comp.VerifyEmitDiagnostics(expected); + + comp = CreateRuntimeAsyncCompilation(src + Buffer4Definition); + comp.VerifyEmitDiagnostics(expected); } [ConditionalFact(typeof(CoreClrOnly))] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs index 254772b5d70..8824e0d7f91 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs @@ -6643,6 +6643,137 @@ .locals init (CustomHandler V_0) "); } + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/79888")] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PassAsRefWithoutKeyword_05_WithReorder(string refKind) + { + var code = $$""" + BadFunc( + message: $"abc", + value2: 0, + value1: 1); + + static void BadFunc( + {{refKind}} InterpolatedStringHandler message, + in int? value1, + int? value2) + { + System.Console.Write(value1); + System.Console.Write(value2); + } + + [System.Runtime.CompilerServices.InterpolatedStringHandler] + public ref struct InterpolatedStringHandler + { + public InterpolatedStringHandler( + int literalLength, + int formattedCount) + { + } + + public void AppendLiteral(string value) + { + System.Console.Write(value); + } + } + """; + + var verifier = CompileAndVerify(code, targetFramework: TargetFramework.Net90, expectedOutput: ExecutionConditionUtil.IsCoreClr ? "abc10" : null, verify: Verification.FailsPEVerify); + verifier.VerifyIL("", $$""" + { + // Code size 47 (0x2f) + .maxstack 3 + .locals init (int? V_0, + InterpolatedStringHandler V_1, + int? V_2) + IL_0000: ldloca.s V_1 + IL_0002: ldc.i4.3 + IL_0003: ldc.i4.0 + IL_0004: call "InterpolatedStringHandler..ctor(int, int)" + IL_0009: ldloca.s V_1 + IL_000b: ldstr "abc" + IL_0010: call "void InterpolatedStringHandler.AppendLiteral(string)" + IL_0015: ldloca.s V_1 + IL_0017: ldloca.s V_0 + IL_0019: ldc.i4.0 + IL_001a: call "int?..ctor(int)" + IL_001f: ldc.i4.1 + IL_0020: newobj "int?..ctor(int)" + IL_0025: stloc.2 + IL_0026: ldloca.s V_2 + IL_0028: ldloc.0 + IL_0029: call "void Program.<
$>g__BadFunc|0_0({{refKind}} InterpolatedStringHandler, in int?, int?)" + IL_002e: ret + } + """); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/79888")] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PassAsRefWithoutKeyword_05_WithoutReorder(string refKind) + { + var code = $$""" + BadFunc( + message: $"abc", + value1: 1, + value2: 0); + + static void BadFunc( + {{refKind}} InterpolatedStringHandler message, + in int? value1, + int? value2) + { + System.Console.Write(value1); + System.Console.Write(value2); + } + + [System.Runtime.CompilerServices.InterpolatedStringHandler] + public ref struct InterpolatedStringHandler + { + public InterpolatedStringHandler( + int literalLength, + int formattedCount) + { + } + + public void AppendLiteral(string value) + { + System.Console.Write(value); + } + } + """; + + var verifier = CompileAndVerify(code, targetFramework: TargetFramework.Net90, expectedOutput: ExecutionConditionUtil.IsCoreClr ? "abc10" : null, verify: Verification.FailsPEVerify); + verifier.VerifyIL("", $$""" + { + // Code size 44 (0x2c) + .maxstack 3 + .locals init (InterpolatedStringHandler V_0, + int? V_1) + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.3 + IL_0003: ldc.i4.0 + IL_0004: call "InterpolatedStringHandler..ctor(int, int)" + IL_0009: ldloca.s V_0 + IL_000b: ldstr "abc" + IL_0010: call "void InterpolatedStringHandler.AppendLiteral(string)" + IL_0015: ldloca.s V_0 + IL_0017: ldc.i4.1 + IL_0018: newobj "int?..ctor(int)" + IL_001d: stloc.1 + IL_001e: ldloca.s V_1 + IL_0020: ldc.i4.0 + IL_0021: newobj "int?..ctor(int)" + IL_0026: call "void Program.<
$>g__BadFunc|0_0({{refKind}} InterpolatedStringHandler, in int?, int?)" + IL_002b: ret + } + """); + } + [Theory] [CombinatorialData] public void RefOverloadResolution_Struct([CombinatorialValues("in", "ref")] string refKind, [CombinatorialValues(@"$""{1,2:f}Literal""", @"$""{1,2:f}"" + $""Literal""")] string expression) diff --git a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs index 1e610d2734b..60f800d5fab 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs @@ -6105,5 +6105,41 @@ public static void ConvertAll(IEnumerable values) ReceiverType.Name: nameof(Enumerable), }); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79930")] + public void NotInvoicableInvocationTarget() + { + var source = +@" +public class OuterClass +{ + void M() + { + var s = OuterClass.InnerClass(); + } + + public class InnerClass + { + } +} +"; + var comp = CreateCompilation(source); + + comp.VerifyDiagnostics( + // (6,28): error CS1955: Non-invocable member 'OuterClass.InnerClass' cannot be used like a method. + // var s = OuterClass.InnerClass(); + Diagnostic(ErrorCode.ERR_NonInvocableMemberCalled, "InnerClass").WithArguments("OuterClass.InnerClass").WithLocation(6, 28) + ); + + var tree = comp.SyntaxTrees[0]; + var semanticModel = comp.GetSemanticModel(tree); + var invocationNode = tree.GetRoot().DescendantNodes().OfType().Single(); + var expressionNode = invocationNode.Expression; + + var info = semanticModel.GetSymbolInfo(expressionNode); + Assert.Null(info.Symbol); + Assert.Equal(CandidateReason.NotInvocable, info.CandidateReason); + Assert.Same(comp.GetTypeByMetadataName("OuterClass+InnerClass").GetPublicSymbol(), info.CandidateSymbols.Single()); + } } } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs index ffd9ddaf95b..7197502def3 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs @@ -268,14 +268,18 @@ class K "; var semanticInfo = GetSemanticInfoForTest(sourceCode); - Assert.Null(semanticInfo.Type); - Assert.Null(semanticInfo.ConvertedType); + Assert.Equal("System.Int32", semanticInfo.Type.ToTestDisplayString()); + Assert.Equal(TypeKind.Struct, semanticInfo.Type.TypeKind); + Assert.Equal("System.Int32", semanticInfo.ConvertedType.ToTestDisplayString()); + Assert.Equal(TypeKind.Struct, semanticInfo.ConvertedType.TypeKind); Assert.Equal(ConversionKind.Identity, semanticInfo.ImplicitConversion.Kind); Assert.Null(semanticInfo.Symbol); - Assert.Equal(CandidateReason.None, semanticInfo.CandidateReason); - // Tracked by https://github.com/dotnet/roslyn/issues/78957 : public API, see if we can restore a behavior closer to previous (ie. returning the field as candidate symbol) - Assert.Empty(semanticInfo.CandidateSymbols); + Assert.Equal(CandidateReason.NotInvocable, semanticInfo.CandidateReason); + Assert.Equal(1, semanticInfo.CandidateSymbols.Length); + var sortedCandidates = semanticInfo.CandidateSymbols.OrderBy(s => s.ToTestDisplayString(), StringComparer.Ordinal).ToArray(); + Assert.Equal("System.Int32 K.f", sortedCandidates[0].ToTestDisplayString()); + Assert.Equal(SymbolKind.Field, sortedCandidates[0].Kind); Assert.Equal(0, semanticInfo.MethodGroup.Length); Assert.False(semanticInfo.IsCompileTimeConstant); diff --git a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests_MissingIdentifiers.cs b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests_MissingIdentifiers.cs index a0a35785bfa..21a2604675a 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests_MissingIdentifiers.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests_MissingIdentifiers.cs @@ -4209,5 +4209,2816 @@ file struct { } } EOF(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Fixed() + { + UsingDeclaration(""" + void M() + { + List + fixed + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,10): error CS1003: Syntax error, '(' expected + // fixed + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(4, 10), + // (4,10): error CS1031: Type expected + // fixed + Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(4, 10), + // (4,10): error CS1001: Identifier expected + // fixed + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(4, 10), + // (4,10): error CS1003: Syntax error, ',' expected + // fixed + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(4, 10), + // (5,2): error CS1026: ) expected + // } + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(5, 2), + // (5,2): error CS1733: Expected expression + // } + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(5, 2), + // (5,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 2), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.FixedStatement); + { + N(SyntaxKind.FixedKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.VariableDeclaration); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Fixed_DoubleGeneric() + { + UsingDeclaration(""" + void M() + { + List> + fixed + } + """, + options: null, + // (3,21): error CS1525: Invalid expression term 'fixed' + // List> + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("fixed").WithLocation(3, 21), + // (3,21): error CS1002: ; expected + // List> + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 21), + // (4,10): error CS1003: Syntax error, '(' expected + // fixed + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(4, 10), + // (4,10): error CS1031: Type expected + // fixed + Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(4, 10), + // (4,10): error CS1001: Identifier expected + // fixed + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(4, 10), + // (4,10): error CS1003: Syntax error, ',' expected + // fixed + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(4, 10), + // (5,2): error CS1026: ) expected + // } + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(5, 2), + // (5,2): error CS1733: Expected expression + // } + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(5, 2), + // (5,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 2), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "List"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "List"); + } + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.RightShiftExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanGreaterThanToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.FixedStatement); + { + N(SyntaxKind.FixedKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.VariableDeclaration); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Break() + { + UsingDeclaration(""" + void M() + { + List + break + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,10): error CS1002: ; expected + // break + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 10)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Continue() + { + UsingDeclaration(""" + void M() + { + List + continue + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,13): error CS1002: ; expected + // continue + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 13)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.ContinueStatement); + { + N(SyntaxKind.ContinueKeyword); + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Try() + { + UsingDeclaration(""" + void M() + { + List + try + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,8): error CS1514: { expected + // try + Diagnostic(ErrorCode.ERR_LbraceExpected, "").WithLocation(4, 8), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.TryStatement); + { + N(SyntaxKind.TryKeyword); + N(SyntaxKind.Block); + { + M(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + M(SyntaxKind.FinallyClause); + { + M(SyntaxKind.FinallyKeyword); + M(SyntaxKind.Block); + { + M(SyntaxKind.OpenBraceToken); + M(SyntaxKind.CloseBraceToken); + } + } + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Do() + { + UsingDeclaration(""" + void M() + { + List + do + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,7): error CS1525: Invalid expression term '}' + // do + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 7), + // (4,7): error CS1002: ; expected + // do + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 7), + // (4,7): error CS1003: Syntax error, 'while' expected + // do + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("while").WithLocation(4, 7), + // (4,7): error CS1003: Syntax error, '(' expected + // do + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(4, 7), + // (4,7): error CS1525: Invalid expression term '}' + // do + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 7), + // (4,7): error CS1026: ) expected + // do + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(4, 7), + // (4,7): error CS1002: ; expected + // do + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 7)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.DoStatement); + { + N(SyntaxKind.DoKeyword); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + M(SyntaxKind.WhileKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_For() + { + UsingDeclaration(""" + void M() + { + List + for + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,8): error CS1003: Syntax error, '(' expected + // for + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(4, 8), + // (4,8): error CS1001: Identifier expected + // for + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(4, 8), + // (5,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 2), + // (5,2): error CS1733: Expected expression + // } + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(5, 2), + // (5,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 2), + // (5,2): error CS1026: ) expected + // } + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(5, 2), + // (5,2): error CS1733: Expected expression + // } + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(5, 2), + // (5,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 2), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.SemicolonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Foreach() + { + UsingDeclaration(""" + void M() + { + List + foreach + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,12): error CS1003: Syntax error, '(' expected + // foreach + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(4, 12), + // (4,12): error CS1525: Invalid expression term '}' + // foreach + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 12), + // (4,12): error CS1515: 'in' expected + // foreach + Diagnostic(ErrorCode.ERR_InExpected, "").WithLocation(4, 12), + // (4,12): error CS0230: Type and identifier are both required in a foreach statement + // foreach + Diagnostic(ErrorCode.ERR_BadForeachDecl, "").WithLocation(4, 12), + // (4,12): error CS1525: Invalid expression term '}' + // foreach + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 12), + // (4,12): error CS1026: ) expected + // foreach + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(4, 12), + // (4,12): error CS1525: Invalid expression term '}' + // foreach + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 12), + // (4,12): error CS1002: ; expected + // foreach + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 12)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.ForEachVariableStatement); + { + N(SyntaxKind.ForEachKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.InKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Goto() + { + UsingDeclaration(""" + void M() + { + List + goto + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,9): error CS1001: Identifier expected + // goto + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(4, 9), + // (4,9): error CS1002: ; expected + // goto + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 9)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.GotoStatement); + { + N(SyntaxKind.GotoKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_If() + { + UsingDeclaration(""" + void M() + { + List + if + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,7): error CS1003: Syntax error, '(' expected + // if + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(4, 7), + // (4,7): error CS1525: Invalid expression term '}' + // if + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 7), + // (4,7): error CS1026: ) expected + // if + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(4, 7), + // (4,7): error CS1525: Invalid expression term '}' + // if + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 7), + // (4,7): error CS1002: ; expected + // if + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 7)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.IfStatement); + { + N(SyntaxKind.IfKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Else() + { + UsingDeclaration(""" + void M() + { + List + else + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (3,15): error CS8641: 'else' cannot start a statement. + // List + Diagnostic(ErrorCode.ERR_ElseCannotStartStatement, "").WithLocation(3, 15), + // (3,15): error CS1003: Syntax error, '(' expected + // List + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(3, 15), + // (3,15): error CS1525: Invalid expression term 'else' + // List + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("else").WithLocation(3, 15), + // (3,15): error CS1026: ) expected + // List + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(3, 15), + // (3,15): error CS1525: Invalid expression term 'else' + // List + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("else").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,9): error CS1525: Invalid expression term '}' + // else + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 9), + // (4,9): error CS1002: ; expected + // else + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 9)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.IfStatement); + { + M(SyntaxKind.IfKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.ElseClause); + { + N(SyntaxKind.ElseKeyword); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Lock() + { + UsingDeclaration(""" + void M() + { + List + lock + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,9): error CS1003: Syntax error, '(' expected + // lock + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(4, 9), + // (4,9): error CS1525: Invalid expression term '}' + // lock + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 9), + // (4,9): error CS1026: ) expected + // lock + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(4, 9), + // (4,9): error CS1525: Invalid expression term '}' + // lock + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 9), + // (4,9): error CS1002: ; expected + // lock + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 9)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.LockStatement); + { + N(SyntaxKind.LockKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Return() + { + UsingDeclaration(""" + void M() + { + List + return + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,11): error CS1525: Invalid expression term '}' + // return + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 11), + // (4,11): error CS1002: ; expected + // return + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 11)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.ReturnStatement); + { + N(SyntaxKind.ReturnKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Switch() + { + UsingDeclaration(""" + void M() + { + List + switch + } + """, + options: null, + // (3,15): error CS1525: Invalid expression term 'switch' + // List + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("switch").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,11): error CS1525: Invalid expression term '}' + // switch + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 11), + // (4,11): error CS8515: Parentheses are required around the switch governing expression. + // switch + Diagnostic(ErrorCode.ERR_SwitchGoverningExpressionRequiresParens, "").WithLocation(4, 11), + // (4,11): error CS1514: { expected + // switch + Diagnostic(ErrorCode.ERR_LbraceExpected, "").WithLocation(4, 11), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "List"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.GreaterThanToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Unsafe() + { + UsingDeclaration(""" + void M() + { + List + unsafe + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,5): error CS0106: The modifier 'unsafe' is not valid for this item + // unsafe + Diagnostic(ErrorCode.ERR_BadMemberFlag, "unsafe").WithArguments("unsafe").WithLocation(4, 5), + // (4,11): error CS1031: Type expected + // unsafe + Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(4, 11), + // (4,11): error CS1001: Identifier expected + // unsafe + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(4, 11), + // (4,11): error CS1003: Syntax error, ',' expected + // unsafe + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(4, 11), + // (5,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 2), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.UnsafeKeyword); + M(SyntaxKind.VariableDeclaration); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Using() + { + UsingDeclaration(""" + void M() + { + List + using + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,10): error CS1031: Type expected + // using + Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(4, 10), + // (4,10): error CS1001: Identifier expected + // using + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(4, 10), + // (4,10): error CS1003: Syntax error, ',' expected + // using + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(4, 10), + // (5,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 2), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.UsingKeyword); + M(SyntaxKind.VariableDeclaration); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_While() + { + UsingDeclaration(""" + void M() + { + List + while + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,10): error CS1003: Syntax error, '(' expected + // while + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(4, 10), + // (4,10): error CS1525: Invalid expression term '}' + // while + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 10), + // (4,10): error CS1026: ) expected + // while + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(4, 10), + // (4,10): error CS1525: Invalid expression term '}' + // while + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 10), + // (4,10): error CS1002: ; expected + // while + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 10)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.WhileStatement); + { + N(SyntaxKind.WhileKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Volatile() + { + UsingDeclaration(""" + void M() + { + List + volatile + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,5): error CS0106: The modifier 'volatile' is not valid for this item + // volatile + Diagnostic(ErrorCode.ERR_BadMemberFlag, "volatile").WithArguments("volatile").WithLocation(4, 5), + // (4,13): error CS1031: Type expected + // volatile + Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(4, 13), + // (4,13): error CS1001: Identifier expected + // volatile + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(4, 13), + // (4,13): error CS1003: Syntax error, ',' expected + // volatile + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(4, 13), + // (5,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 2), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VolatileKeyword); + M(SyntaxKind.VariableDeclaration); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Extern() + { + UsingDeclaration(""" + void M() + { + List + extern + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,5): error CS0106: The modifier 'extern' is not valid for this item + // extern + Diagnostic(ErrorCode.ERR_BadMemberFlag, "extern").WithArguments("extern").WithLocation(4, 5), + // (4,11): error CS1031: Type expected + // extern + Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(4, 11), + // (4,11): error CS1001: Identifier expected + // extern + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(4, 11), + // (4,11): error CS1003: Syntax error, ',' expected + // extern + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(4, 11), + // (5,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 2), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.ExternKeyword); + M(SyntaxKind.VariableDeclaration); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Case() + { + UsingDeclaration(""" + void M() + { + List + case + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (3,15): error CS1003: Syntax error, 'switch' expected + // List + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("switch").WithLocation(3, 15), + // (4,9): error CS8504: Pattern missing + // case + Diagnostic(ErrorCode.ERR_MissingPattern, "").WithLocation(4, 9), + // (4,9): error CS1003: Syntax error, ':' expected + // case + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(4, 9), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.SwitchStatement); + { + M(SyntaxKind.SwitchKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CaseSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.ColonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + [InlineData("checked", SyntaxKind.CheckedExpression, SyntaxKind.CheckedKeyword)] + [InlineData("unchecked", SyntaxKind.UncheckedExpression, SyntaxKind.UncheckedKeyword)] + public void DefiniteStatementAfterGenericType_Checked(string keyword, SyntaxKind expressionKind, SyntaxKind tokenKind) + { + UsingDeclaration($$""" + void M() + { + List {{keyword}}(1); + } + """); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "List"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.GreaterThanToken); + N(expressionKind); + { + N(tokenKind); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Throw() + { + UsingDeclaration($$""" + void M() + { + List throw ex; + } + """, + options: null, + // (3,16): error CS1525: Invalid expression term 'throw' + // List throw ex; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "throw ex").WithArguments("throw").WithLocation(3, 16)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "List"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.ThrowExpression); + { + N(SyntaxKind.ThrowKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "ex"); + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_OpenBrace() + { + UsingDeclaration($$""" + void M() + { + List { + } + """, + options: null, + // (3,16): error CS1002: ; expected + // List { + Diagnostic(ErrorCode.ERR_SemicolonExpected, "{").WithLocation(3, 16), + // (4,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(4, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Semicolon() + { + UsingDeclaration($$""" + void M() + { + List; + } + """); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Static() + { + UsingDeclaration($$""" + void M() + { + List static + } + """, + null, + // (3,16): error CS1525: Invalid expression term 'static' + // List static + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "static").WithArguments("static").WithLocation(3, 16), + // (3,16): error CS1002: ; expected + // List static + Diagnostic(ErrorCode.ERR_SemicolonExpected, "static").WithLocation(3, 16), + // (3,16): error CS0106: The modifier 'static' is not valid for this item + // List static + Diagnostic(ErrorCode.ERR_BadMemberFlag, "static").WithArguments("static").WithLocation(3, 16), + // (3,22): error CS1031: Type expected + // List static + Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(3, 22), + // (3,22): error CS1001: Identifier expected + // List static + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 22), + // (3,22): error CS1003: Syntax error, ',' expected + // List static + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(3, 22), + // (4,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 2), + // (4,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(4, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "List"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.GreaterThanToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.StaticKeyword); + M(SyntaxKind.VariableDeclaration); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_ReadOnlyKeyword() + { + UsingDeclaration($$""" + void M() + { + List readonly + } + """, + options: null, + // (3,16): error CS1525: Invalid expression term 'readonly' + // List readonly + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "readonly").WithArguments("readonly").WithLocation(3, 16), + // (3,16): error CS1002: ; expected + // List readonly + Diagnostic(ErrorCode.ERR_SemicolonExpected, "readonly").WithLocation(3, 16), + // (3,16): error CS0106: The modifier 'readonly' is not valid for this item + // List readonly + Diagnostic(ErrorCode.ERR_BadMemberFlag, "readonly").WithArguments("readonly").WithLocation(3, 16), + // (3,24): error CS1031: Type expected + // List readonly + Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(3, 24), + // (3,24): error CS1001: Identifier expected + // List readonly + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 24), + // (3,24): error CS1003: Syntax error, ',' expected + // List readonly + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(3, 24), + // (4,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 2), + // (4,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(4, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "List"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.GreaterThanToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.ReadOnlyKeyword); + M(SyntaxKind.VariableDeclaration); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Ref() + { + UsingDeclaration($$""" + void M() + { + List ref + } + """, + options: null, + // (3,16): error CS1525: Invalid expression term 'ref' + // List ref + Diagnostic(ErrorCode.ERR_InvalidExprTerm, """ + ref + + """).WithArguments("ref").WithLocation(3, 16), + // (3,19): error CS1525: Invalid expression term '}' + // List ref + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(3, 19), + // (3,19): error CS1002: ; expected + // List ref + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 19)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "List"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Bracket() + { + UsingDeclaration($$""" + void M() + { + List [ + } + """, + options: null, + // (3,17): error CS1001: Identifier expected + // List [ + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 17), + // (4,2): error CS1003: Syntax error, ']' expected + // } + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("]").WithLocation(4, 2), + // (4,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 2), + // (4,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(4, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.ElementAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + M(SyntaxKind.CloseBracketToken); + } + } + M(SyntaxKind.SemicolonToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Dot() + { + UsingDeclaration($$""" + void M() + { + List . + } + """, + options: null, + // (3,17): error CS1001: Identifier expected + // List . + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 17), + // (3,17): error CS1002: ; expected + // List . + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 17)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + [InlineData("class", SyntaxKind.ClassDeclaration, SyntaxKind.ClassKeyword)] + [InlineData("struct", SyntaxKind.StructDeclaration, SyntaxKind.StructKeyword)] + [InlineData("interface", SyntaxKind.InterfaceDeclaration, SyntaxKind.InterfaceKeyword)] + [InlineData("enum", SyntaxKind.EnumDeclaration, SyntaxKind.EnumKeyword)] + public void DefiniteStatementAfterGenericType_TypeDecl(string typeText, SyntaxKind declarationKind, SyntaxKind keywordKind) + { + int column = 1 + typeText.Length; + UsingTree($$""" + List + + {{typeText}} + """, + options: null, + // (1,11): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(1, 11), + // (1,11): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 11), + // (3,7): error CS1001: Identifier expected + // struct + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, column), + // (3,7): error CS1514: { expected + // struct + Diagnostic(ErrorCode.ERR_LbraceExpected, "").WithLocation(3, column), + // (3,7): error CS1513: } expected + // struct + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(3, column)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(declarationKind); + { + N(keywordKind); + M(SyntaxKind.IdentifierToken); + M(SyntaxKind.OpenBraceToken); + M(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_RecordDecl() + { + UsingTree($$""" + List + + record + """, + options: null, + // (3,7): error CS1002: ; expected + // record + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 7)); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "record"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_RecordDecl2() + { + UsingTree($$""" + List + + record TypeName + """, + options: null, + // (3,8): error CS1003: Syntax error, ',' expected + // record TypeName + Diagnostic(ErrorCode.ERR_SyntaxError, "TypeName").WithArguments(",").WithLocation(3, 8), + // (3,16): error CS1002: ; expected + // record TypeName + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 16)); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "record"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_DelegateDecl() + { + UsingTree($$""" + List + + delegate + """, + options: null, + // (1,11): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(1, 11), + // (1,11): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 11), + // (3,9): error CS1031: Type expected + // delegate + Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(3, 9), + // (3,9): error CS1001: Identifier expected + // delegate + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 9), + // (3,9): error CS1003: Syntax error, '(' expected + // delegate + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(3, 9), + // (3,9): error CS1026: ) expected + // delegate + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(3, 9), + // (3,9): error CS1002: ; expected + // delegate + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 9)); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.DelegateDeclaration); + { + N(SyntaxKind.DelegateKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.IdentifierToken); + M(SyntaxKind.ParameterList); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + [InlineData("public", SyntaxKind.PublicKeyword)] + [InlineData("internal", SyntaxKind.InternalKeyword)] + [InlineData("private", SyntaxKind.PrivateKeyword)] + [InlineData("protected", SyntaxKind.ProtectedKeyword)] + public void DefiniteStatementAfterGenericType_Accessibility(string accessibilityText, SyntaxKind accessibilityKind) + { + UsingTree($$""" + List + + {{accessibilityText}} + """, + options: null, + // (3,1): error CS1585: Member modifier 'internal' must precede the member type and name + // internal + Diagnostic(ErrorCode.ERR_BadModifierLocation, accessibilityText).WithArguments(accessibilityText).WithLocation(3, 1), + // (3,1): error CS0116: A namespace cannot directly contain members such as fields, methods or statements + // internal + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, accessibilityText).WithLocation(3, 1)); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + } + N(SyntaxKind.IncompleteMember); + { + N(accessibilityKind); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } } } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs index 55c9fc7d560..150061b6944 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs @@ -16,102 +16,68 @@ public class ExtensionsParsingTests : ParsingTests { public ExtensionsParsingTests(ITestOutputHelper output) : base(output) { } - [Fact] - public void LangVer13() + [Theory, CombinatorialData] + public void LangVer_01(bool useCSharp14) { - // Tracked by https://github.com/dotnet/roslyn/issues/78961 : consider giving a LangVer error to trigger UpgradeProject - UsingTree(""" -class C + var src = """ +static class C { extension(object o) where T : struct { } } -""", - TestOptions.Regular13, - // (3,17): error CS1519: Invalid token '(' in class, record, struct, or interface member declaration - // extension(object o) where T : struct { } - Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "(").WithArguments("(").WithLocation(3, 17), - // (3,26): error CS8124: Tuple must contain at least two elements. - // extension(object o) where T : struct { } - Diagnostic(ErrorCode.ERR_TupleTooFewElements, ")").WithLocation(3, 26), - // (3,34): error CS1002: ; expected - // extension(object o) where T : struct { } - Diagnostic(ErrorCode.ERR_SemicolonExpected, "T").WithLocation(3, 34), - // (3,36): error CS1519: Invalid token ':' in class, record, struct, or interface member declaration - // extension(object o) where T : struct { } - Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ":").WithArguments(":").WithLocation(3, 36), - // (3,36): error CS1519: Invalid token ':' in class, record, struct, or interface member declaration - // extension(object o) where T : struct { } - Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ":").WithArguments(":").WithLocation(3, 36), - // (3,45): error CS1001: Identifier expected +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.Regular13); + comp.VerifyEmitDiagnostics( + // (3,5): error CS9260: Feature 'extensions' is not available in C# 13.0. Please use language version 14.0 or greater. // extension(object o) where T : struct { } - Diagnostic(ErrorCode.ERR_IdentifierExpected, "{").WithLocation(3, 45)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion13, "extension").WithArguments("extensions", "14.0").WithLocation(3, 5)); + UsingTree(src, TestOptions.Regular13); N(SyntaxKind.CompilationUnit); { N(SyntaxKind.ClassDeclaration); { + N(SyntaxKind.StaticKeyword); N(SyntaxKind.ClassKeyword); N(SyntaxKind.IdentifierToken, "C"); N(SyntaxKind.OpenBraceToken); - N(SyntaxKind.IncompleteMember); + N(SyntaxKind.ExtensionBlockDeclaration); { - N(SyntaxKind.GenericName); + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.TypeParameterList); { - N(SyntaxKind.IdentifierToken, "extension"); - N(SyntaxKind.TypeArgumentList); + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); { - N(SyntaxKind.LessThanToken); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "T"); - } - N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.IdentifierToken, "T"); } + N(SyntaxKind.GreaterThanToken); } - } - N(SyntaxKind.FieldDeclaration); - { - N(SyntaxKind.VariableDeclaration); + N(SyntaxKind.ParameterList); { - N(SyntaxKind.TupleType); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); { - N(SyntaxKind.OpenParenToken); - N(SyntaxKind.TupleElement); - { - N(SyntaxKind.PredefinedType); - { - N(SyntaxKind.ObjectKeyword); - } - N(SyntaxKind.IdentifierToken, "o"); - } - M(SyntaxKind.CommaToken); - M(SyntaxKind.TupleElement); + N(SyntaxKind.PredefinedType); { - M(SyntaxKind.IdentifierName); - { - M(SyntaxKind.IdentifierToken); - } + N(SyntaxKind.ObjectKeyword); } - N(SyntaxKind.CloseParenToken); - } - N(SyntaxKind.VariableDeclarator); - { - N(SyntaxKind.IdentifierToken, "where"); + N(SyntaxKind.IdentifierToken, "o"); } + N(SyntaxKind.CloseParenToken); } - M(SyntaxKind.SemicolonToken); - } - N(SyntaxKind.IncompleteMember); - { - N(SyntaxKind.IdentifierName); + N(SyntaxKind.TypeParameterConstraintClause); { - N(SyntaxKind.IdentifierToken, "T"); + N(SyntaxKind.WhereKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.StructConstraint); + { + N(SyntaxKind.StructKeyword); + } } - } - N(SyntaxKind.StructDeclaration); - { - N(SyntaxKind.StructKeyword); - M(SyntaxKind.IdentifierToken); N(SyntaxKind.OpenBraceToken); N(SyntaxKind.CloseBraceToken); } @@ -120,23 +86,13 @@ class C N(SyntaxKind.EndOfFileToken); } EOF(); - } - - [Theory, CombinatorialData] - public void LangVer14(bool useCSharp14) - { - UsingTree(""" -class C -{ - extension(object o) where T : struct { } -} -""", - useCSharp14 ? TestOptions.Regular14 : TestOptions.RegularPreview); + UsingTree(src, useCSharp14 ? TestOptions.Regular14 : TestOptions.RegularPreview); N(SyntaxKind.CompilationUnit); { N(SyntaxKind.ClassDeclaration); { + N(SyntaxKind.StaticKeyword); N(SyntaxKind.ClassKeyword); N(SyntaxKind.IdentifierToken, "C"); N(SyntaxKind.OpenBraceToken); @@ -188,6 +144,449 @@ class C EOF(); } + [Theory, CombinatorialData] + public void LangVer_02(bool useCSharp14) + { + // Without type parameters + var src = """ +static class C +{ + extension(object o) { } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.Regular13); + comp.VerifyEmitDiagnostics( + // (3,5): error CS9260: Feature 'extensions' is not available in C# 13.0. Please use language version 14.0 or greater. + // extension(object o) { } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion13, "extension(object o) { }").WithArguments("extensions", "14.0").WithLocation(3, 5), + // (3,5): error CS0710: Static classes cannot have instance constructors + // extension(object o) { } + Diagnostic(ErrorCode.ERR_ConstructorInStaticClass, "extension").WithLocation(3, 5)); + + UsingTree(src, TestOptions.Regular13); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.IdentifierToken, "extension"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "o"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + + UsingTree(src, useCSharp14 ? TestOptions.Regular14 : TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionBlockDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "o"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void LangVer_03() + { + // Without type parameters, escaped identifier + var src = """ +static class C +{ + @extension(object o) { } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.Regular13); + comp.VerifyEmitDiagnostics( + // (3,5): error CS1520: Method must have a return type + // @extension(object o) { } + Diagnostic(ErrorCode.ERR_MemberNeedsType, "@extension").WithLocation(3, 5), + // (3,5): error CS0710: Static classes cannot have instance constructors + // @extension(object o) { } + Diagnostic(ErrorCode.ERR_ConstructorInStaticClass, "@extension").WithLocation(3, 5)); + + UsingTree(src, TestOptions.Regular13); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.IdentifierToken, "@extension"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "o"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void LangVer_04(bool useCSharp14) + { + // Without parameter list + var src = """ +class C +{ + extension { } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.Regular13); + comp.VerifyEmitDiagnostics( + // (3,15): error CS1519: Invalid token '{' in a member declaration + // extension { } + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "{").WithArguments("{").WithLocation(3, 15), + // (3,15): error CS1519: Invalid token '{' in a member declaration + // extension { } + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "{").WithArguments("{").WithLocation(3, 15), + // (4,1): error CS1022: Type or namespace definition, or end-of-file expected + // } + Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(4, 1)); + + UsingTree(src, TestOptions.Regular13, + // (3,15): error CS1519: Invalid token '{' in a member declaration + // extension { } + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "{").WithArguments("{").WithLocation(3, 15), + // (3,15): error CS1519: Invalid token '{' in a member declaration + // extension { } + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "{").WithArguments("{").WithLocation(3, 15), + // (4,1): error CS1022: Type or namespace definition, or end-of-file expected + // } + Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(4, 1)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "extension"); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + + UsingTree(src, useCSharp14 ? TestOptions.Regular14 : TestOptions.RegularPreview, + // (3,15): error CS1003: Syntax error, '(' expected + // extension { } + Diagnostic(ErrorCode.ERR_SyntaxError, "{").WithArguments("(").WithLocation(3, 15), + // (3,15): error CS1031: Type expected + // extension { } + Diagnostic(ErrorCode.ERR_TypeExpected, "{").WithLocation(3, 15), + // (3,15): error CS1026: ) expected + // extension { } + Diagnostic(ErrorCode.ERR_CloseParenExpected, "{").WithLocation(3, 15)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionBlockDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + M(SyntaxKind.ParameterList); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.Parameter); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void LangVer_05(bool useCSharp14) + { + // Top-level + var src = """ +extension(object o) { } +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.Regular13); + comp.VerifyEmitDiagnostics( + // (1,1): error CS9260: Feature 'extensions' is not available in C# 13.0. Please use language version 14.0 or greater. + // extension(object o) { } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion13, "extension").WithArguments("extensions", "14.0").WithLocation(1, 1), + // (1,1): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(object o) { } + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(1, 1)); + + UsingTree(src, TestOptions.Regular13); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ExtensionBlockDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "o"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + + UsingTree(src, useCSharp14 ? TestOptions.Regular14 : TestOptions.RegularPreview); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ExtensionBlockDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "o"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void LangVer_06(bool useCSharp14) + { + // Top-level, without type parameters + var src = """ +extension(object o) { } +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.Regular13); + comp.VerifyEmitDiagnostics( + // (1,1): error CS0103: The name 'extension' does not exist in the current context + // extension(object o) { } + Diagnostic(ErrorCode.ERR_NameNotInContext, "extension").WithArguments("extension").WithLocation(1, 1), + // (1,11): error CS1525: Invalid expression term 'object' + // extension(object o) { } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "object").WithArguments("object").WithLocation(1, 11), + // (1,18): error CS1003: Syntax error, ',' expected + // extension(object o) { } + Diagnostic(ErrorCode.ERR_SyntaxError, "o").WithArguments(",").WithLocation(1, 18), + // (1,18): error CS0103: The name 'o' does not exist in the current context + // extension(object o) { } + Diagnostic(ErrorCode.ERR_NameNotInContext, "o").WithArguments("o").WithLocation(1, 18), + // (1,21): error CS1002: ; expected + // extension(object o) { } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "{").WithLocation(1, 21)); + + UsingTree(src, TestOptions.Regular13, + // (1,11): error CS1525: Invalid expression term 'object' + // extension(object o) { } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "object").WithArguments("object").WithLocation(1, 11), + // (1,18): error CS1003: Syntax error, ',' expected + // extension(object o) { } + Diagnostic(ErrorCode.ERR_SyntaxError, "o").WithArguments(",").WithLocation(1, 18), + // (1,21): error CS1002: ; expected + // extension(object o) { } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "{").WithLocation(1, 21)); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "extension"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "o"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + + UsingTree(src, useCSharp14 ? TestOptions.Regular14 : TestOptions.RegularPreview); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ExtensionBlockDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "o"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + [Fact] public void MultipleConstraints() { diff --git a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs index cde011bb78e..b05dc0f3771 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs @@ -6197,5 +6197,46 @@ void M6() } """); } + + [Fact] + public void TestNormalizeExtension_01() + { + TestNormalizeDeclaration(""" +static class E +{ +extension < T > ( int i ) where T : struct +{ +} +} +""", """ +static class E +{ + extension(int i) + where T : struct + { + } +} +"""); + } + + [Fact] + public void TestNormalizeExtension_02() + { + TestNormalizeDeclaration(""" +static class E +{ +extension ( int ) +{ +} +} +""", """ +static class E +{ + extension(int) + { + } +} +"""); + } } } diff --git a/src/roslyn/src/Compilers/CSharp/csc/AnyCpu/csc.csproj b/src/roslyn/src/Compilers/CSharp/csc/AnyCpu/csc.csproj index 9ab6763f608..5708cc5a911 100644 --- a/src/roslyn/src/Compilers/CSharp/csc/AnyCpu/csc.csproj +++ b/src/roslyn/src/Compilers/CSharp/csc/AnyCpu/csc.csproj @@ -4,8 +4,8 @@ Exe $(NetRoslynSourceBuild);net472 - false true + false diff --git a/src/roslyn/src/Compilers/Core/CodeAnalysisTest/Diagnostics/DiagnosticLocalizationTests.cs b/src/roslyn/src/Compilers/Core/CodeAnalysisTest/Diagnostics/DiagnosticLocalizationTests.cs index 9848453306a..6c9685563c0 100644 --- a/src/roslyn/src/Compilers/Core/CodeAnalysisTest/Diagnostics/DiagnosticLocalizationTests.cs +++ b/src/roslyn/src/Compilers/Core/CodeAnalysisTest/Diagnostics/DiagnosticLocalizationTests.cs @@ -304,11 +304,21 @@ private static void TestDescriptorIsExceptionSafeCore(DiagnosticDescriptor descr Action onAnalyzerException = (ex, a, diag, ct) => exceptionDiagnostics.Add(diag); var analyzerManager = new AnalyzerManager(analyzer); var compilation = CSharp.CSharpCompilation.Create("test"); - var analyzerExecutor = AnalyzerExecutor.Create(compilation, AnalyzerOptions.Empty, - addNonCategorizedDiagnostic: (_, _) => { }, onAnalyzerException, analyzerExceptionFilter: null, - isCompilerAnalyzer: _ => false, analyzerManager, shouldSkipAnalysisOnGeneratedCode: _ => false, - shouldSuppressGeneratedCodeDiagnostic: (_, _, _, _) => false, isGeneratedCodeLocation: (_, _, _) => false, - isAnalyzerSuppressedForTree: (_, _, _, _) => false, getAnalyzerGate: _ => null, + var analyzerExecutor = AnalyzerExecutor.Create( + compilation, + AnalyzerOptions.Empty, + addNonCategorizedDiagnostic: (_, _, _) => { }, + onAnalyzerException, + analyzerExceptionFilter: null, + isCompilerAnalyzer: _ => false, + diagnosticAnalyzers: [analyzer], + getAnalyzerConfigOptionsProvider: null, + analyzerManager, + shouldSkipAnalysisOnGeneratedCode: _ => false, + shouldSuppressGeneratedCodeDiagnostic: (_, _, _, _) => false, + isGeneratedCodeLocation: (_, _, _) => false, + isAnalyzerSuppressedForTree: (_, _, _, _) => false, + getAnalyzerGate: _ => null, getSemanticModel: tree => compilation.GetSemanticModel(tree, ignoreAccessibility: true), SeverityFilter.None); var descriptors = analyzerManager.GetSupportedDiagnosticDescriptors(analyzer, analyzerExecutor, CancellationToken.None); diff --git a/src/roslyn/src/Compilers/Core/MSBuildTask/InteractiveCompiler.cs b/src/roslyn/src/Compilers/Core/MSBuildTask/InteractiveCompiler.cs index c26a3cb0f2d..9d1229e43be 100644 --- a/src/roslyn/src/Compilers/Core/MSBuildTask/InteractiveCompiler.cs +++ b/src/roslyn/src/Compilers/Core/MSBuildTask/InteractiveCompiler.cs @@ -16,8 +16,6 @@ namespace Microsoft.CodeAnalysis.BuildTasks ///
public abstract class InteractiveCompiler : ManagedToolTask { - internal readonly PropertyDictionary _store = new PropertyDictionary(); - public InteractiveCompiler() : base(ErrorString.ResourceManager) { diff --git a/src/roslyn/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs b/src/roslyn/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs index 89e34f57541..d7e52b89bd5 100644 --- a/src/roslyn/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs +++ b/src/roslyn/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs @@ -51,7 +51,6 @@ private enum CompilationKind } private CancellationTokenSource? _sharedCompileCts; - internal readonly PropertyDictionary _store = new PropertyDictionary(); internal abstract RequestLanguage Language { get; } diff --git a/src/roslyn/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs b/src/roslyn/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs index 6fb01d315c4..58c3450e683 100644 --- a/src/roslyn/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs +++ b/src/roslyn/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs @@ -10,12 +10,16 @@ using System.Text; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; +using Microsoft.CodeAnalysis.CommandLine; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.BuildTasks { public abstract class ManagedToolTask : ToolTask { + private bool? _useAppHost; + internal readonly PropertyDictionary _store = new PropertyDictionary(); + /// /// A copy of this task, compiled for .NET Framework, is deployed into the .NET SDK. It is a bridge task /// that is loaded into .NET Framework MSBuild but launches the .NET Core compiler. This task necessarily @@ -53,6 +57,23 @@ public abstract class ManagedToolTask : ToolTask internal string PathToBuiltInTool => Path.Combine(GetToolDirectory(), ToolName); + /// + /// We fallback to not use the apphost if it is not present (can happen in compiler toolset scenarios for example). + /// + private bool UseAppHost + { + get + { + if (_useAppHost is not { } useAppHost) + { + _useAppHost = useAppHost = File.Exists(Path.Combine(GetToolDirectory(), AppHostToolName)); + Debug.Assert(IsBuiltinToolRunningOnCoreClr || useAppHost); + } + + return useAppHost; + } + } + protected ManagedToolTask(ResourceManager resourceManager) : base(resourceManager) { @@ -78,7 +99,8 @@ internal string GenerateToolArguments() protected sealed override string GenerateCommandLineCommands() { var commandLineArguments = GenerateToolArguments(); - if (UsingBuiltinTool && IsBuiltinToolRunningOnCoreClr) + + if (UsingBuiltinTool && !UseAppHost) { commandLineArguments = RuntimeHostInfo.GetDotNetExecCommandLine(PathToBuiltInTool, commandLineArguments); } @@ -115,15 +137,17 @@ protected sealed override string GenerateResponseFileCommands() /// /// This generates the path to the executable that is directly ran. - /// This could be the managed assembly itself (on desktop .NET on Windows), - /// or a runtime such as dotnet. + /// This could be the executable apphost or a runtime such as dotnet. /// - protected sealed override string GenerateFullPathToTool() => (UsingBuiltinTool, IsBuiltinToolRunningOnCoreClr) switch + protected sealed override string GenerateFullPathToTool() { - (true, true) => RuntimeHostInfo.GetDotNetPathOrDefault(), - (true, false) => PathToBuiltInTool, - (false, _) => Path.Combine(ToolPath ?? "", ToolExe) - }; + if (UsingBuiltinTool) + { + return UseAppHost ? PathToBuiltInTool : RuntimeHostInfo.GetDotNetPathOrDefault(); + } + + return Path.Combine(ToolPath ?? "", ToolExe); + } protected abstract string ToolNameWithoutExtension { get; } @@ -139,10 +163,15 @@ protected sealed override string GenerateResponseFileCommands() /// It returns the name of the managed assembly, which might not be the path returned by /// GenerateFullPathToTool, which can return the path to e.g. the dotnet executable. /// - protected sealed override string ToolName => - IsBuiltinToolRunningOnCoreClr - ? $"{ToolNameWithoutExtension}.dll" - : $"{ToolNameWithoutExtension}.exe"; + protected sealed override string ToolName + { + get + { + return UseAppHost ? AppHostToolName : $"{ToolNameWithoutExtension}.dll"; + } + } + + private string AppHostToolName => $"{ToolNameWithoutExtension}{PlatformInformation.ExeExtension}"; /// /// This generates the command line arguments passed to the tool. @@ -206,5 +235,17 @@ internal static string GetBuildTaskDirectory() return buildTaskDirectory; } + + protected override bool ValidateParameters() + { + // Set DOTNET_ROOT so that the apphost executables launch properly. + if (RuntimeHostInfo.GetToolDotNetRoot() is { } dotNetRoot) + { + Log.LogMessage("Setting {0} to '{1}'", RuntimeHostInfo.DotNetRootEnvironmentName, dotNetRoot); + EnvironmentVariables = [.. EnvironmentVariables ?? [], $"{RuntimeHostInfo.DotNetRootEnvironmentName}={dotNetRoot}"]; + } + + return base.ValidateParameters(); + } } } diff --git a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/CscTests.cs b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/CscTests.cs index 8325b71fcf0..c59e16bdd28 100644 --- a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/CscTests.cs +++ b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/CscTests.cs @@ -6,6 +6,7 @@ using System.IO; using Microsoft.CodeAnalysis.BuildTasks.UnitTests.TestUtilities; using Roslyn.Test.Utilities; +using Roslyn.Utilities; using Xunit; using Xunit.Abstractions; @@ -484,14 +485,13 @@ public void EmptyCscToolExe() csc.ToolExe = ""; csc.Sources = MSBuildUtil.CreateTaskItems("test.cs"); Assert.Equal("", csc.GenerateCommandLineContents()); - // StartsWith because it can be csc.exe or csc.dll - Assert.StartsWith(Path.Combine("path", "to", "custom_csc", "csc."), csc.GeneratePathToTool()); + AssertEx.Equal(Path.Combine("path", "to", "custom_csc", $"csc{PlatformInformation.ExeExtension}"), csc.GeneratePathToTool()); csc = new Csc(); csc.ToolPath = Path.Combine("path", "to", "custom_csc"); csc.Sources = MSBuildUtil.CreateTaskItems("test.cs"); Assert.Equal("", csc.GenerateCommandLineContents()); - Assert.StartsWith(Path.Combine("path", "to", "custom_csc", "csc."), csc.GeneratePathToTool()); + AssertEx.Equal(Path.Combine("path", "to", "custom_csc", $"csc{PlatformInformation.ExeExtension}"), csc.GeneratePathToTool()); } [Fact] diff --git a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.cs b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.cs index cfebef67d98..c85de16d2f2 100644 --- a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.cs +++ b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.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. -#if NET472 using System.Collections.Generic; using System.IO; using Microsoft.CodeAnalysis.Test.Utilities; @@ -24,24 +23,6 @@ private ProcessResult RunCompilerOutput(TempFile file) return ProcessUtilities.Run(file.Path, "", Path.GetDirectoryName(file.Path)); } - private static void VerifyResult(ProcessResult result) - { - Assert.Equal("", result.Output); - Assert.Equal("", result.Errors); - Assert.Equal(0, result.ExitCode); - } - - private void VerifyResultAndOutput(ProcessResult result, TempDirectory path, string expectedOutput) - { - using (var resultFile = GetResultFile(path, "hello.exe")) - { - VerifyResult(result); - - var runningResult = RunCompilerOutput(resultFile); - Assert.Equal(expectedOutput, runningResult.Output); - } - } - // A dictionary with name and contents of all the files we want to create for the SimpleMSBuild test. private Dictionary SimpleMsBuildFiles => new Dictionary { { "HelloSolution.sln", @@ -536,7 +517,7 @@ public void ReportAnalyzerMSBuild() string arguments = string.Format(@"/m /nr:false /t:Rebuild /p:UseSharedCompilation=false /p:UseRoslyn=1 HelloSolution.sln"); var result = RunCommandLineCompiler(_msbuildExecutable, arguments, _tempDirectory, ReportAnalyzerMsBuildFiles, new Dictionary - { { "MyMSBuildToolsPath", Path.GetDirectoryName(typeof(IntegrationTests).Assembly.Location) } }); + { { "MyMSBuildToolsPath", Path.GetDirectoryName(typeof(IntegrationTests).Assembly.Location)! } }); Assert.True(result.ExitCode != 0); Assert.Contains("/reportanalyzer", result.Output); @@ -717,4 +698,3 @@ public class Class1 } } } -#endif diff --git a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/MSBuildManagedToolTests.cs b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/MSBuildManagedToolTests.cs index 4e69936b4e6..3a55c89cf10 100644 --- a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/MSBuildManagedToolTests.cs +++ b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/MSBuildManagedToolTests.cs @@ -2,9 +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; using System.IO; -using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.BuildTasks.UnitTests; @@ -16,7 +15,7 @@ public void PathToBuiltinTool() { var taskPath = Path.GetDirectoryName(typeof(ManagedCompiler).Assembly.Location)!; var relativePath = RuntimeHostInfo.IsCoreClrRuntime - ? Path.Combine("bincore", "csc.dll") + ? Path.Combine("bincore", $"csc{PlatformInformation.ExeExtension}") : "csc.exe"; var task = new Csc(); Assert.Equal(Path.Combine(taskPath, relativePath), task.PathToBuiltInTool); diff --git a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/Microsoft.Build.Tasks.CodeAnalysis.UnitTests.csproj b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/Microsoft.Build.Tasks.CodeAnalysis.UnitTests.csproj index c4e50a85580..be45ea70de1 100644 --- a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/Microsoft.Build.Tasks.CodeAnalysis.UnitTests.csproj +++ b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/Microsoft.Build.Tasks.CodeAnalysis.UnitTests.csproj @@ -69,6 +69,7 @@ <_CommandLineCompilerReferenceOutputPath>@(CommandLineCompilerReference->'%(RootDir)%(Directory)*.*') + <_LinkPrefix Condition="'$(TargetFramework)' != 'net472'">bincore\ <_CommandLineCompilerReferenceContent Include="$(_CommandLineCompilerReferenceOutputPath)" /> @@ -78,7 +79,7 @@ - + diff --git a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/TestUtilities/IntegrationTestBase.cs b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/TestUtilities/IntegrationTestBase.cs index a2af1e8b354..7be928d9fe0 100644 --- a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/TestUtilities/IntegrationTestBase.cs +++ b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/TestUtilities/IntegrationTestBase.cs @@ -2,9 +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. -#if NET472 - -using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -19,10 +16,12 @@ public abstract class IntegrationTestBase : TestBase { protected static readonly string? s_msbuildDirectory; +#if NET472 static IntegrationTestBase() { s_msbuildDirectory = DesktopTestHelpers.GetMSBuildDirectory(); } +#endif protected readonly ITestOutputHelper _output; protected readonly string? _msbuildExecutable; @@ -89,6 +88,105 @@ protected static ProcessResult RunCommandLineCompiler( additionalEnvironmentVars: AddForLoggingEnvironmentVars(additionalEnvironmentVars)); } + protected ProcessResult? RunMsbuild( + string arguments, + TempDirectory currentDirectory, + IEnumerable> filesInDirectory, + IEnumerable>? additionalEnvironmentVars = null) + { + if (_msbuildExecutable != null) + { + return RunCommandLineCompiler( + _msbuildExecutable, + arguments, + currentDirectory, + filesInDirectory, + additionalEnvironmentVars); + } + + if (ExecutionConditionUtil.IsDesktop) + { + _output.WriteLine("Skipping because Framework MSBuild is missing, this is a desktop test, " + + "and we cannot use the desktop Csc/Vbc task from 'dotnet msbuild', i.e., Core MSBuild."); + return null; + } + + return RunCommandLineCompiler( + "dotnet", + $"msbuild {arguments}", + currentDirectory, + filesInDirectory, + additionalEnvironmentVars); + } + + [Theory, CombinatorialData] + public void SdkBuild_Csc(bool useSharedCompilation) + { + var result = RunMsbuild( + "/v:n /m /nr:false /t:Build /restore Test.csproj", + _tempDirectory, + new Dictionary + { + { "File.cs", """ + class Program { static void Main() { System.Console.WriteLine("Hello from file"); } } + """ }, + { "Test.csproj", $""" + + + + netstandard2.0 + {useSharedCompilation} + + + """ }, + }); + + if (result == null) return; + + _output.WriteLine(result.Output); + + Assert.Equal(0, result.ExitCode); + Assert.Contains(useSharedCompilation ? "server processed compilation" : "using command line tool by design", result.Output); + Assert.DoesNotContain("csc.dll", result.Output); + Assert.Contains(ExecutionConditionUtil.IsWindows ? "csc.exe" : "csc", result.Output); + } + + [Theory, CombinatorialData] + public void SdkBuild_Vbc(bool useSharedCompilation) + { + var result = RunMsbuild( + "/v:n /m /nr:false /t:Build /restore Test.vbproj", + _tempDirectory, + new Dictionary + { + { "File.vb", """ + Public Module Program + Public Sub Main() + System.Console.WriteLine("Hello from file") + End Sub + End Module + """ }, + { "Test.vbproj", $""" + + + + netstandard2.0 + {useSharedCompilation} + + + """ }, + }); + + if (result == null) return; + + _output.WriteLine(result.Output); + + Assert.Equal(0, result.ExitCode); + Assert.Contains(useSharedCompilation ? "server processed compilation" : "using command line tool by design", result.Output); + Assert.DoesNotContain("vbc.dll", result.Output); + Assert.Contains(ExecutionConditionUtil.IsWindows ? "vbc.exe" : "vbc", result.Output); + } + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/79907")] public void StdLib_Csc(bool useSharedCompilation, bool disableSdkPath) { @@ -170,5 +268,3 @@ End Module } } } - -#endif diff --git a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/TestUtilities/TaskTestUtil.cs b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/TestUtilities/TaskTestUtil.cs index c5cab3e2671..617c848f16a 100644 --- a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/TestUtilities/TaskTestUtil.cs +++ b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/TestUtilities/TaskTestUtil.cs @@ -26,7 +26,7 @@ public static void AssertCommandLine( Assert.Equal(expected, task.GenerateCommandLineArgsTaskItems(rsp).Select(x => x.ItemSpec)); #if NET - Assert.Equal($"exec \"{task.PathToBuiltInTool}\"", task.GenerateCommandLineContents().Trim()); + Assert.Empty(task.GenerateCommandLineContents().Trim()); // Can only run the Execute path on .NET Core presently. The internal workings of ToolTask // will fail if it can't find the tool exe and we don't have csc.exe, vbc.exe, etc ... @@ -40,7 +40,7 @@ public static void AssertCommandLine( var message = engine.BuildMessages.OfType().Single(); var commandLine = message.CommandLine.Replace(" ", " ").Trim(); - Assert.Equal($@"{RuntimeHostInfo.GetDotNetPathOrDefault()} exec ""{task.PathToBuiltInTool}"" {line}", commandLine); + AssertEx.Equal($@"{task.PathToBuiltInTool} {line}", commandLine); compilerTask.NoConfig = true; Assert.Equal("/noconfig", compilerTask.GenerateToolArguments()); diff --git a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisScope.cs b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisScope.cs index d7f251955cd..938087e5f90 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisScope.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisScope.cs @@ -95,20 +95,19 @@ internal class AnalysisScope public static AnalysisScope Create(Compilation compilation, ImmutableArray analyzers, CompilationWithAnalyzers compilationWithAnalyzers) { - var analyzerOptions = compilationWithAnalyzers.AnalysisOptions.Options; + var additionalFiles = compilationWithAnalyzers.AnalysisOptions.Options.GetAdditionalFiles(); var hasAllAnalyzers = ComputeHasAllAnalyzers(analyzers, compilationWithAnalyzers); var concurrentAnalysis = compilationWithAnalyzers.AnalysisOptions.ConcurrentAnalysis; - return Create(compilation, analyzerOptions, analyzers, hasAllAnalyzers, concurrentAnalysis); + return Create(compilation, additionalFiles, analyzers, hasAllAnalyzers, concurrentAnalysis); } - public static AnalysisScope CreateForBatchCompile(Compilation compilation, AnalyzerOptions analyzerOptions, ImmutableArray analyzers) + public static AnalysisScope CreateForBatchCompile(Compilation compilation, ImmutableArray additionalFiles, ImmutableArray analyzers) { - return Create(compilation, analyzerOptions, analyzers, hasAllAnalyzers: true, concurrentAnalysis: compilation.Options.ConcurrentBuild); + return Create(compilation, additionalFiles, analyzers, hasAllAnalyzers: true, concurrentAnalysis: compilation.Options.ConcurrentBuild); } - private static AnalysisScope Create(Compilation compilation, AnalyzerOptions? analyzerOptions, ImmutableArray analyzers, bool hasAllAnalyzers, bool concurrentAnalysis) + private static AnalysisScope Create(Compilation compilation, ImmutableArray additionalFiles, ImmutableArray analyzers, bool hasAllAnalyzers, bool concurrentAnalysis) { - var additionalFiles = analyzerOptions?.AdditionalFiles ?? ImmutableArray.Empty; return new AnalysisScope(compilation.CommonSyntaxTrees, additionalFiles, analyzers, hasAllAnalyzers, filterFile: null, filterSpanOpt: null, originalFilterFile: null, originalFilterSpan: null, isSyntacticSingleFileAnalysis: false, diff --git a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs index 84e42332b22..385e075a73c 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs @@ -35,6 +35,11 @@ internal abstract partial class AnalyzerDriver : IDisposable private static readonly Func s_IsCompilerAnalyzerFunc = IsCompilerAnalyzer; private static readonly Func s_getTopmostNodeForAnalysis = GetTopmostNodeForAnalysis; + /// + /// Separate pool for diagnostic analyzers as these collections commonly exceed ArrayBuilder's size threshold + /// + private static readonly ObjectPool> s_diagnosticAnalyzerPool = new ObjectPool>(() => new ArrayBuilder()); + private readonly Func _isGeneratedCode; /// @@ -471,23 +476,29 @@ internal void Initialize( var diagnosticQueue = DiagnosticQueue.Create(categorizeDiagnostics); var suppressedDiagnosticIds = trackSuppressedDiagnosticIds ? new ConcurrentSet() : null; - Action? addNotCategorizedDiagnostic = null; - Action? addCategorizedLocalDiagnostic = null; - Action? addCategorizedNonLocalDiagnostic = null; + Action? addNotCategorizedDiagnostic = null; + Action? addCategorizedLocalDiagnostic = null; + Action? addCategorizedNonLocalDiagnostic = null; if (categorizeDiagnostics) { - addCategorizedLocalDiagnostic = GetDiagnosticSink(diagnosticQueue.EnqueueLocal, compilation, analysisOptions.Options, _severityFilter, suppressedDiagnosticIds); - addCategorizedNonLocalDiagnostic = GetDiagnosticSink(diagnosticQueue.EnqueueNonLocal, compilation, analysisOptions.Options, _severityFilter, suppressedDiagnosticIds); + addCategorizedLocalDiagnostic = GetDiagnosticSink(diagnosticQueue.EnqueueLocal, compilation, _severityFilter, suppressedDiagnosticIds); + addCategorizedNonLocalDiagnostic = GetDiagnosticSink(diagnosticQueue.EnqueueNonLocal, compilation, _severityFilter, suppressedDiagnosticIds); } else { - addNotCategorizedDiagnostic = GetDiagnosticSink(diagnosticQueue.Enqueue, compilation, analysisOptions.Options, _severityFilter, suppressedDiagnosticIds); + addNotCategorizedDiagnostic = GetDiagnosticSink(diagnosticQueue.Enqueue, compilation, _severityFilter, suppressedDiagnosticIds); } // Wrap onAnalyzerException to pass in filtered diagnostic. - Action newOnAnalyzerException = (ex, analyzer, diagnostic, cancellationToken) => + var options = analysisOptions.Options ?? AnalyzerOptions.Empty; + + var newOnAnalyzerException = (Exception ex, DiagnosticAnalyzer analyzer, Diagnostic diagnostic, CancellationToken cancellationToken) => { - var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation, analysisOptions.Options, _severityFilter, suppressedDiagnosticIds, cancellationToken); + // Note: in this callback, it's fine/correct to use analysisOptions.Options instead of any diagnostic analyzer + // specific options. That's because the options are only used for filtering/determining-severities. But we + // do not allow analyzers to control the filtering/severity around the reporting of analyzer exceptions themselves. + // These are always passed through and reported to the user. + var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation, options, _severityFilter, suppressedDiagnosticIds, cancellationToken); if (filteredDiagnostic != null) { if (analysisOptions.OnAnalyzerException != null) @@ -496,18 +507,19 @@ internal void Initialize( } else if (categorizeDiagnostics) { - addCategorizedNonLocalDiagnostic!(filteredDiagnostic, analyzer, cancellationToken); + addCategorizedNonLocalDiagnostic!(filteredDiagnostic, analyzer, options, cancellationToken); } else { - addNotCategorizedDiagnostic!(filteredDiagnostic, cancellationToken); + addNotCategorizedDiagnostic!(filteredDiagnostic, options, cancellationToken); } } }; var analyzerExecutor = AnalyzerExecutor.Create( - compilation, analysisOptions.Options ?? AnalyzerOptions.Empty, addNotCategorizedDiagnostic, newOnAnalyzerException, analysisOptions.AnalyzerExceptionFilter, - IsCompilerAnalyzer, AnalyzerManager, ShouldSkipAnalysisOnGeneratedCode, ShouldSuppressGeneratedCodeDiagnostic, IsGeneratedOrHiddenCodeLocation, IsAnalyzerSuppressedForTree, GetAnalyzerGate, + compilation, options, addNotCategorizedDiagnostic, newOnAnalyzerException, analysisOptions.AnalyzerExceptionFilter, + IsCompilerAnalyzer, analysisScope.Analyzers, analysisOptions.GetAnalyzerConfigOptionsProvider, + AnalyzerManager, ShouldSkipAnalysisOnGeneratedCode, ShouldSuppressGeneratedCodeDiagnostic, IsGeneratedOrHiddenCodeLocation, IsAnalyzerSuppressedForTree, GetAnalyzerGate, getSemanticModel: GetOrCreateSemanticModel, _severityFilter, analysisOptions.LogAnalyzerExecutionTime, addCategorizedLocalDiagnostic, addCategorizedNonLocalDiagnostic, s => _programmaticSuppressions!.Add(s)); @@ -847,7 +859,7 @@ internal static AnalyzerDriver CreateAndAttachToCompilation( var categorizeDiagnostics = false; var analysisOptions = new CompilationWithAnalyzersOptions(options, onAnalyzerException, analyzerExceptionFilter: analyzerExceptionFilter, concurrentAnalysis: true, logAnalyzerExecutionTime: reportAnalyzer, reportSuppressedDiagnostics: false); - var analysisScope = AnalysisScope.CreateForBatchCompile(newCompilation, options, analyzers); + var analysisScope = AnalysisScope.CreateForBatchCompile(newCompilation, options.GetAdditionalFiles(), analyzers); analyzerDriver.Initialize(newCompilation, analysisOptions, new CompilationData(newCompilation), analysisScope, categorizeDiagnostics, trackSuppressedDiagnosticIds, cancellationToken); analyzerDriver.AttachQueueAndStartProcessingEvents(newCompilation.EventQueue!, analysisScope, usingPrePopulatedEventQueue: false, cancellationToken); @@ -1823,7 +1835,7 @@ private bool TryExecuteSymbolEndActions( } var success = true; - var completedAnalyzers = ArrayBuilder.GetInstance(); + ArrayBuilder completedAnalyzers = s_diagnosticAnalyzerPool.Allocate(); var processedAnalyzers = PooledHashSet.GetInstance(); try { @@ -1876,7 +1888,12 @@ private bool TryExecuteSymbolEndActions( finally { processedAnalyzers.Free(); - completedAnalyzers.Free(); + + // Do not call completedAnalyzers.Free, as the ArrayBuilder isn't associated with our pool and even if it were, we don't + // want the default freeing behavior of limiting pooled array size to ArrayBuilder.PooledArrayLengthLimitExclusive. + // Instead, we need to explicitly add this item back to our pool. + completedAnalyzers.Clear(); + s_diagnosticAnalyzerPool.Free(completedAnalyzers); } } @@ -1952,9 +1969,9 @@ private void ExecuteCompilationActions( } } - internal static Action GetDiagnosticSink(Action addDiagnosticCore, Compilation compilation, AnalyzerOptions? analyzerOptions, SeverityFilter severityFilter, ConcurrentSet? suppressedDiagnosticIds) + internal static Action GetDiagnosticSink(Action addDiagnosticCore, Compilation compilation, SeverityFilter severityFilter, ConcurrentSet? suppressedDiagnosticIds) { - return (diagnostic, cancellationToken) => + return (diagnostic, analyzerOptions, cancellationToken) => { var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation, analyzerOptions, severityFilter, suppressedDiagnosticIds, cancellationToken); if (filteredDiagnostic != null) @@ -1964,9 +1981,9 @@ internal static Action GetDiagnosticSink(Action GetDiagnosticSink(Action addLocalDiagnosticCore, Compilation compilation, AnalyzerOptions? analyzerOptions, SeverityFilter severityFilter, ConcurrentSet? suppressedDiagnosticIds) + internal static Action GetDiagnosticSink(Action addLocalDiagnosticCore, Compilation compilation, SeverityFilter severityFilter, ConcurrentSet? suppressedDiagnosticIds) { - return (diagnostic, analyzer, isSyntaxDiagnostic, cancellationToken) => + return (diagnostic, analyzer, analyzerOptions, isSyntaxDiagnostic, cancellationToken) => { var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation, analyzerOptions, severityFilter, suppressedDiagnosticIds, cancellationToken); if (filteredDiagnostic != null) @@ -1976,9 +1993,9 @@ internal static Action }; } - internal static Action GetDiagnosticSink(Action addDiagnosticCore, Compilation compilation, AnalyzerOptions? analyzerOptions, SeverityFilter severityFilter, ConcurrentSet? suppressedDiagnosticIds) + internal static Action GetDiagnosticSink(Action addDiagnosticCore, Compilation compilation, SeverityFilter severityFilter, ConcurrentSet? suppressedDiagnosticIds) { - return (diagnostic, analyzer, cancellationToken) => + return (diagnostic, analyzer, analyzerOptions, cancellationToken) => { var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation, analyzerOptions, severityFilter, suppressedDiagnosticIds, cancellationToken); if (filteredDiagnostic != null) diff --git a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.AnalyzerDiagnosticReporter.cs b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.AnalyzerDiagnosticReporter.cs index f8730fa78dc..aa593ed94e3 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.AnalyzerDiagnosticReporter.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.AnalyzerDiagnosticReporter.cs @@ -29,10 +29,11 @@ public static AnalyzerDiagnosticReporter GetInstance( TextSpan? span, Compilation compilation, DiagnosticAnalyzer analyzer, + AnalyzerOptions analyzerOptions, bool isSyntaxDiagnostic, - Action? addNonCategorizedDiagnostic, - Action? addCategorizedLocalDiagnostic, - Action? addCategorizedNonLocalDiagnostic, + Action? addNonCategorizedDiagnostic, + Action? addCategorizedLocalDiagnostic, + Action? addCategorizedNonLocalDiagnostic, Func shouldSuppressGeneratedCodeDiagnostic, CancellationToken cancellationToken) { @@ -41,6 +42,7 @@ public static AnalyzerDiagnosticReporter GetInstance( item.FilterSpanForLocalDiagnostics = span; item._compilation = compilation; item._analyzer = analyzer; + item._analyzerOptions = analyzerOptions; item._isSyntaxDiagnostic = isSyntaxDiagnostic; item._addNonCategorizedDiagnostic = addNonCategorizedDiagnostic; item._addCategorizedLocalDiagnostic = addCategorizedLocalDiagnostic; @@ -56,6 +58,7 @@ public void Free() FilterSpanForLocalDiagnostics = null; _compilation = null!; _analyzer = null!; + _analyzerOptions = null!; _isSyntaxDiagnostic = default; _addNonCategorizedDiagnostic = null!; _addCategorizedLocalDiagnostic = null!; @@ -68,10 +71,11 @@ public void Free() private SourceOrAdditionalFile? _contextFile; private Compilation _compilation; private DiagnosticAnalyzer _analyzer; + private AnalyzerOptions _analyzerOptions; private bool _isSyntaxDiagnostic; - private Action? _addNonCategorizedDiagnostic; - private Action? _addCategorizedLocalDiagnostic; - private Action? _addCategorizedNonLocalDiagnostic; + private Action? _addNonCategorizedDiagnostic; + private Action? _addCategorizedLocalDiagnostic; + private Action? _addCategorizedNonLocalDiagnostic; private Func _shouldSuppressGeneratedCodeDiagnostic; private CancellationToken _cancellationToken; @@ -102,7 +106,7 @@ private void AddDiagnostic(Diagnostic diagnostic) if (_addCategorizedLocalDiagnostic == null) { Debug.Assert(_addNonCategorizedDiagnostic != null); - _addNonCategorizedDiagnostic(diagnostic, _cancellationToken); + _addNonCategorizedDiagnostic(diagnostic, _analyzerOptions, _cancellationToken); return; } @@ -112,11 +116,11 @@ private void AddDiagnostic(Diagnostic diagnostic) if (isLocalDiagnostic(diagnostic) && (!FilterSpanForLocalDiagnostics.HasValue || FilterSpanForLocalDiagnostics.Value.IntersectsWith(diagnostic.Location.SourceSpan))) { - _addCategorizedLocalDiagnostic(diagnostic, _analyzer, _isSyntaxDiagnostic, _cancellationToken); + _addCategorizedLocalDiagnostic(diagnostic, _analyzer, _analyzerOptions, _isSyntaxDiagnostic, _cancellationToken); } else { - _addCategorizedNonLocalDiagnostic(diagnostic, _analyzer, _cancellationToken); + _addCategorizedNonLocalDiagnostic(diagnostic, _analyzer, _analyzerOptions, _cancellationToken); } return; diff --git a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs index 19d913bc9aa..897a1161edf 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs @@ -31,9 +31,9 @@ internal partial class AnalyzerExecutor internal const string AnalyzerExceptionDiagnosticId = "AD0001"; internal const string AnalyzerDriverExceptionDiagnosticId = "AD0002"; - private readonly Action? _addNonCategorizedDiagnostic; - private readonly Action? _addCategorizedLocalDiagnostic; - private readonly Action? _addCategorizedNonLocalDiagnostic; + private readonly Action? _addNonCategorizedDiagnostic; + private readonly Action? _addCategorizedLocalDiagnostic; + private readonly Action? _addCategorizedNonLocalDiagnostic; private readonly Action? _addSuppression; private readonly Func? _analyzerExceptionFilter; private readonly AnalyzerManager _analyzerManager; @@ -51,6 +51,17 @@ internal partial class AnalyzerExecutor private readonly ConcurrentDictionary>? _analyzerExecutionTimeMap; private readonly CompilationAnalysisValueProviderFactory _compilationAnalysisValueProviderFactory; + /// + /// Cache of analyzer to analyzer specific options. If there are no specific + /// options, and the shared options should be used for all analyzers. This is the common case, which + /// means we don't pay for the extra indirection of a dictionary lookup normally. + /// + /// Note: this map is generated + /// at construction time, and is unchanging after that point. So it can be safely read from multiple + /// threads without need for locks. + /// + private readonly Dictionary? _analyzerToCachedOptions; + private Func? _lazyGetControlFlowGraph; private ConcurrentDictionary? _lazyControlFlowGraphMap; @@ -79,6 +90,10 @@ private bool IsAnalyzerSuppressedForTree(DiagnosticAnalyzer analyzer, SyntaxTree /// /// Delegate to determine if the given analyzer is compiler analyzer. /// We need to special case the compiler analyzer at few places for performance reasons. + /// Analyzers to query for custom options if is provided. + /// Optional callback to allow individual configuration options + /// on a per analyzer basis. /// Analyzer manager to fetch supported diagnostics. /// /// Delegate to fetch the gate object to guard all callbacks into the analyzer. @@ -98,10 +113,12 @@ private bool IsAnalyzerSuppressedForTree(DiagnosticAnalyzer analyzer, SyntaxTree public static AnalyzerExecutor Create( Compilation compilation, AnalyzerOptions analyzerOptions, - Action? addNonCategorizedDiagnostic, + Action? addNonCategorizedDiagnostic, Action onAnalyzerException, Func? analyzerExceptionFilter, Func isCompilerAnalyzer, + ImmutableArray diagnosticAnalyzers, + Func? getAnalyzerConfigOptionsProvider, AnalyzerManager analyzerManager, Func shouldSkipAnalysisOnGeneratedCode, Func shouldSuppressGeneratedCodeDiagnostic, @@ -111,8 +128,8 @@ public static AnalyzerExecutor Create( Func getSemanticModel, SeverityFilter severityFilter, bool logExecutionTime = false, - Action? addCategorizedLocalDiagnostic = null, - Action? addCategorizedNonLocalDiagnostic = null, + Action? addCategorizedLocalDiagnostic = null, + Action? addCategorizedNonLocalDiagnostic = null, Action? addSuppression = null) { // We can either report categorized (local/non-local) diagnostics or non-categorized diagnostics. @@ -122,7 +139,7 @@ public static AnalyzerExecutor Create( var analyzerExecutionTimeMap = logExecutionTime ? new ConcurrentDictionary>() : null; return new AnalyzerExecutor(compilation, analyzerOptions, addNonCategorizedDiagnostic, onAnalyzerException, analyzerExceptionFilter, - isCompilerAnalyzer, analyzerManager, shouldSkipAnalysisOnGeneratedCode, shouldSuppressGeneratedCodeDiagnostic, isGeneratedCodeLocation, + isCompilerAnalyzer, diagnosticAnalyzers, getAnalyzerConfigOptionsProvider, analyzerManager, shouldSkipAnalysisOnGeneratedCode, shouldSuppressGeneratedCodeDiagnostic, isGeneratedCodeLocation, isAnalyzerSuppressedForTree, getAnalyzerGate, getSemanticModel, severityFilter, analyzerExecutionTimeMap, addCategorizedLocalDiagnostic, addCategorizedNonLocalDiagnostic, addSuppression); } @@ -130,10 +147,12 @@ public static AnalyzerExecutor Create( private AnalyzerExecutor( Compilation compilation, AnalyzerOptions analyzerOptions, - Action? addNonCategorizedDiagnosticOpt, + Action? addNonCategorizedDiagnosticOpt, Action onAnalyzerException, Func? analyzerExceptionFilter, Func isCompilerAnalyzer, + ImmutableArray diagnosticAnalyzers, + Func? getAnalyzerConfigOptionsProvider, AnalyzerManager analyzerManager, Func shouldSkipAnalysisOnGeneratedCode, Func shouldSuppressGeneratedCodeDiagnostic, @@ -143,8 +162,8 @@ private AnalyzerExecutor( Func getSemanticModel, SeverityFilter severityFilter, ConcurrentDictionary>? analyzerExecutionTimeMap, - Action? addCategorizedLocalDiagnostic, - Action? addCategorizedNonLocalDiagnostic, + Action? addCategorizedLocalDiagnostic, + Action? addCategorizedNonLocalDiagnostic, Action? addSuppression) { Compilation = compilation; @@ -167,6 +186,36 @@ private AnalyzerExecutor( _addSuppression = addSuppression; _compilationAnalysisValueProviderFactory = new CompilationAnalysisValueProviderFactory(); + + if (getAnalyzerConfigOptionsProvider != null) + { + var hasDifferentOptions = false; + + var map = new Dictionary( + capacity: diagnosticAnalyzers.Length, ReferenceEqualityComparer.Instance); + + // Deduping map for the distinct AnalyzerConfigOptionsProvider we get back from getAnalyzerConfigOptionsProvider. + // The common case in VS host is that there is generally only 1-2 of these providers. For example, a provider + // that looks in editorconfig+vsoptions, and a provider that only looks in editorconfig. We only want to make + // a corresponding number of AnalyzerOptions instances for each unique provider we see. + var optionsProviderToOptions = new Dictionary(ReferenceEqualityComparer.Instance); + + foreach (var analyzer in diagnosticAnalyzers) + { + var specificOptionsProvider = getAnalyzerConfigOptionsProvider(analyzer); + var specificOptions = optionsProviderToOptions.GetOrAdd( + specificOptionsProvider, () => analyzerOptions.WithAnalyzerConfigOptionsProvider(specificOptionsProvider)); + map[analyzer] = specificOptions; + + if (specificOptions != analyzerOptions) + hasDifferentOptions = true; + } + + // Only if there is at least one analyzer with specific options, we need to maintain the map. + // Otherwise, we can just toss it and use the shared options for all analyzers. + if (hasDifferentOptions) + _analyzerToCachedOptions = map; + } } internal Compilation Compilation { get; } @@ -218,13 +267,17 @@ public void ExecuteInitializeMethod(HostSessionStartAnalysisScope sessionScope, public void ExecuteCompilationStartActions(ImmutableArray actions, HostCompilationStartAnalysisScope compilationScope, CancellationToken cancellationToken) { // This context doesn't build up any state as we pass it to the Action method of the analyzer. As such, we - // can use the same instance across all actions. + // can use the same instance across all actions as long as the same options are picked for each analyzer. var context = new AnalyzerCompilationStartAnalysisContext(compilationScope, Compilation, AnalyzerOptions, _compilationAnalysisValueProviderFactory, cancellationToken); var contextInfo = new AnalysisContextInfo(Compilation); foreach (var startAction in actions) { + // See if we need to use an analyzer specific options instance. + context = WithAnalyzerSpecificOptions( + context, startAction.Analyzer, static (context, options) => context.WithOptions(options)); + ExecuteAndCatchIfThrows( startAction.Analyzer, static data => data.startAction.Action(data.context), @@ -234,6 +287,24 @@ public void ExecuteCompilationStartActions(ImmutableArray( + TAnalysisContext context, + DiagnosticAnalyzer analyzer, + Func withOptions) + { + // No specific options factory. Can use the shared context. + if (_analyzerToCachedOptions is null) + return context; + + return withOptions(context, GetAnalyzerSpecificOptions(analyzer)); + } + + /// + /// Given an analyzer, returns any specific options for it, or the shared options if none. + /// + private AnalyzerOptions GetAnalyzerSpecificOptions(DiagnosticAnalyzer analyzer) + => _analyzerToCachedOptions?[analyzer] ?? AnalyzerOptions; + /// /// Executes the symbol start actions. /// @@ -258,7 +329,7 @@ public void ExecuteSymbolStartActions( } // This context doesn't build up any state as we pass it to the Action method of the analyzer. As such, we - // can use the same instance across all actions. + // can use the same instance across all actions (as long as the options stay the same per analyzer). var context = new AnalyzerSymbolStartAnalysisContext(symbolScope, symbol, Compilation, AnalyzerOptions, isGeneratedCodeSymbol, filterTree, filterSpan, cancellationToken); var contextInfo = new AnalysisContextInfo(Compilation, symbol); @@ -267,6 +338,10 @@ public void ExecuteSymbolStartActions( { Debug.Assert(startAction.Analyzer == symbolScope.Analyzer); + // See if we need to use an analyzer specific options instance. + context = WithAnalyzerSpecificOptions( + context, startAction.Analyzer, static (context, options) => context.WithOptions(options)); + ExecuteAndCatchIfThrows( startAction.Analyzer, static data => data.startAction.Action(data.context), @@ -300,7 +375,8 @@ public void ExecuteSuppressionAction(DiagnosticSuppressor suppressor, ImmutableA supportedSuppressions, out Func isSupportedSuppression); - var context = new SuppressionAnalysisContext(Compilation, AnalyzerOptions, + var options = GetAnalyzerSpecificOptions(suppressor); + var context = new SuppressionAnalysisContext(Compilation, options, reportedDiagnostics, _addSuppression, isSupportedSuppression, _getSemanticModel, cancellationToken); ExecuteAndCatchIfThrows( @@ -326,7 +402,8 @@ public void ExecuteCompilationActions( { Debug.Assert(compilationEvent is CompilationStartedEvent || compilationEvent is CompilationCompletedEvent); - var addDiagnostic = GetAddCompilationDiagnostic(analyzer, cancellationToken); + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); + var addDiagnostic = GetAddCompilationDiagnostic(analyzer, analyzerOptions, cancellationToken); using var _ = PooledDelegates.GetPooledFunction( static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), @@ -336,7 +413,7 @@ public void ExecuteCompilationActions( // This context doesn't build up any state as we pass it to the Action method of the analyzer. As such, we // can use the same instance across all actions. var context = new CompilationAnalysisContext( - Compilation, AnalyzerOptions, addDiagnostic, + Compilation, analyzerOptions, addDiagnostic, isSupportedDiagnostic, _compilationAnalysisValueProviderFactory, cancellationToken); var contextInfo = new AnalysisContextInfo(Compilation); @@ -380,7 +457,9 @@ public void ExecuteSymbolActions( } var symbol = symbolDeclaredEvent.Symbol; - var addDiagnostic = GetAddDiagnostic(symbol, symbolDeclaredEvent.DeclaringSyntaxReferences, analyzer, getTopMostNodeForAnalysis, cancellationToken); + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); + var addDiagnostic = GetAddDiagnostic( + symbol, symbolDeclaredEvent.DeclaringSyntaxReferences, analyzer, analyzerOptions, getTopMostNodeForAnalysis, cancellationToken); using var _ = PooledDelegates.GetPooledFunction( static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), @@ -390,7 +469,7 @@ public void ExecuteSymbolActions( // This context doesn't build up any state as we pass it to the Action method of the analyzer. As such, we // can use the same instance across all actions. var context = new SymbolAnalysisContext( - symbol, Compilation, AnalyzerOptions, addDiagnostic, + symbol, Compilation, analyzerOptions, addDiagnostic, isSupportedDiagnostic, isGeneratedCodeSymbol, filterTree, filterSpan, cancellationToken); var contextInfo = new AnalysisContextInfo(Compilation, symbol); @@ -490,7 +569,8 @@ private void ExecuteSymbolEndActionsCore( Debug.Assert(!filterSpan.HasValue || filterTree != null); var symbol = symbolDeclaredEvent.Symbol; - var addDiagnostic = GetAddDiagnostic(symbol, symbolDeclaredEvent.DeclaringSyntaxReferences, analyzer, getTopMostNodeForAnalysis, cancellationToken); + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); + var addDiagnostic = GetAddDiagnostic(symbol, symbolDeclaredEvent.DeclaringSyntaxReferences, analyzer, analyzerOptions, getTopMostNodeForAnalysis, cancellationToken); using var _ = PooledDelegates.GetPooledFunction( static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), @@ -499,7 +579,8 @@ private void ExecuteSymbolEndActionsCore( // This context doesn't build up any state as we pass it to the Action method of the analyzer. As such, we // can use the same instance across all actions. - var context = new SymbolAnalysisContext(symbol, Compilation, AnalyzerOptions, addDiagnostic, + var context = new SymbolAnalysisContext( + symbol, Compilation, analyzerOptions, addDiagnostic, isSupportedDiagnostic, isGeneratedCode, filterTree, filterSpan, cancellationToken); var contextInfo = new AnalysisContextInfo(Compilation, symbol); @@ -539,7 +620,8 @@ public void ExecuteSemanticModelActions( return; } - var diagReporter = GetAddSemanticDiagnostic(semanticModel.SyntaxTree, analyzer, cancellationToken); + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); + var diagReporter = GetAddSemanticDiagnostic(semanticModel.SyntaxTree, analyzer, analyzerOptions, cancellationToken); using var _ = PooledDelegates.GetPooledFunction( static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), @@ -549,7 +631,7 @@ public void ExecuteSemanticModelActions( // This context doesn't build up any state as we pass it to the Action method of the analyzer. As such, we // can use the same instance across all actions. var context = new SemanticModelAnalysisContext( - semanticModel, AnalyzerOptions, diagReporter.AddDiagnosticAction, + semanticModel, analyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, filterSpan, isGeneratedCode, cancellationToken); var contextInfo = new AnalysisContextInfo(semanticModel); @@ -592,7 +674,8 @@ public void ExecuteSyntaxTreeActions( return; } - var diagReporter = GetAddSyntaxDiagnostic(file, analyzer, cancellationToken); + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); + var diagReporter = GetAddSyntaxDiagnostic(file, analyzer, analyzerOptions, cancellationToken); using var _ = PooledDelegates.GetPooledFunction( static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), @@ -602,7 +685,7 @@ public void ExecuteSyntaxTreeActions( // This context doesn't build up any state as we pass it to the Action method of the analyzer. As such, we // can use the same instance across all actions. var context = new SyntaxTreeAnalysisContext( - tree, AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, + tree, analyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, Compilation, filterSpan, isGeneratedCode, cancellationToken); var contextInfo = new AnalysisContextInfo(Compilation, file); @@ -637,7 +720,8 @@ public void ExecuteAdditionalFileActions( Debug.Assert(file.AdditionalFile != null); var additionalFile = file.AdditionalFile; - var diagReporter = GetAddSyntaxDiagnostic(file, analyzer, cancellationToken); + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); + var diagReporter = GetAddSyntaxDiagnostic(file, analyzer, analyzerOptions, cancellationToken); using var _ = PooledDelegates.GetPooledFunction( static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), @@ -647,7 +731,7 @@ public void ExecuteAdditionalFileActions( // This context doesn't build up any state as we pass it to the Action method of the analyzer. As such, we // can use the same instance across all actions. var context = new AdditionalFileAnalysisContext( - additionalFile, AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, + additionalFile, analyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, Compilation, filterSpan, cancellationToken); var contextInfo = new AnalysisContextInfo(Compilation, file); @@ -677,7 +761,8 @@ private void ExecuteSyntaxNodeAction( Debug.Assert(!IsAnalyzerSuppressedForTree(syntaxNodeAction.Analyzer, node.SyntaxTree, cancellationToken)); var syntaxNodeContext = new SyntaxNodeAnalysisContext( - node, executionData.DeclaredSymbol, executionData.SemanticModel, AnalyzerOptions, addDiagnostic, + node, executionData.DeclaredSymbol, executionData.SemanticModel, + GetAnalyzerSpecificOptions(syntaxNodeAction.Analyzer), addDiagnostic, isSupportedDiagnostic, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); ExecuteAndCatchIfThrows( @@ -701,7 +786,7 @@ private void ExecuteOperationAction( var operationContext = new OperationAnalysisContext( operation, executionData.DeclaredSymbol, executionData.SemanticModel.Compilation, - AnalyzerOptions, addDiagnostic, isSupportedDiagnostic, GetControlFlowGraph, + GetAnalyzerSpecificOptions(operationAction.Analyzer), addDiagnostic, isSupportedDiagnostic, GetControlFlowGraph, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); ExecuteAndCatchIfThrows( @@ -714,12 +799,14 @@ private void ExecuteOperationAction( private readonly struct ExecutionData( DiagnosticAnalyzer analyzer, + AnalyzerOptions analyzerOptions, ISymbol declaredSymbol, SemanticModel semanticModel, TextSpan? filterSpan, bool isGeneratedCode) { public readonly DiagnosticAnalyzer Analyzer = analyzer; + public readonly AnalyzerOptions AnalyzerOptions = analyzerOptions; public readonly ISymbol DeclaredSymbol = declaredSymbol; public readonly SemanticModel SemanticModel = semanticModel; public readonly TextSpan? FilterSpan = filterSpan; @@ -748,12 +835,13 @@ public void ExecuteCodeBlockActions( // The actions we discover in 'addActions' and then execute in 'executeActions'. var ephemeralActions = ArrayBuilder>.GetInstance(); + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); ExecuteBlockActionsCore( startActions, actions, endActions, declaredNode, - new ExecutionData(analyzer, declaredSymbol, semanticModel, filterSpan, isGeneratedCode), + new ExecutionData(analyzer, analyzerOptions, declaredSymbol, semanticModel, filterSpan, isGeneratedCode), addActions: static (startAction, endActions, executionData, args, cancellationToken) => { var (@this, startActions, executableCodeBlocks, declaredNode, getKind, ephemeralActions) = args; @@ -761,7 +849,7 @@ public void ExecuteCodeBlockActions( var scope = new HostCodeBlockStartAnalysisScope(startAction.Analyzer); var startContext = new AnalyzerCodeBlockStartAnalysisContext( scope, declaredNode, executionData.DeclaredSymbol, executionData.SemanticModel, - @this.AnalyzerOptions, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); + executionData.AnalyzerOptions, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); // Catch Exception from the start action. @this.ExecuteAndCatchIfThrows( @@ -812,7 +900,7 @@ public void ExecuteCodeBlockActions( var (@this, startActions, executableCodeBlocks, declaredNode, getKind, ephemeralActions) = args; var context = new CodeBlockAnalysisContext(declaredNode, executionData.DeclaredSymbol, executionData.SemanticModel, - @this.AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); + executionData.AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); foreach (var blockAction in blockActions) { @@ -850,18 +938,20 @@ public void ExecuteOperationBlockActions( // The actions we discover in 'addActions' and then execute in 'executeActions'. var ephemeralActions = ArrayBuilder.GetInstance(); + + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); ExecuteBlockActionsCore( startActions, actions, endActions, declaredNode, - new ExecutionData(analyzer, declaredSymbol, semanticModel, filterSpan, isGeneratedCode), + new ExecutionData(analyzer, analyzerOptions, declaredSymbol, semanticModel, filterSpan, isGeneratedCode), addActions: static (startAction, endActions, executionData, args, cancellationToken) => { var (@this, startActions, declaredNode, operationBlocks, operations, ephemeralActions) = args; var scope = new HostOperationBlockStartAnalysisScope(startAction.Analyzer); var startContext = new AnalyzerOperationBlockStartAnalysisContext( - scope, operationBlocks, executionData.DeclaredSymbol, executionData.SemanticModel.Compilation, @this.AnalyzerOptions, + scope, operationBlocks, executionData.DeclaredSymbol, executionData.SemanticModel.Compilation, executionData.AnalyzerOptions, @this.GetControlFlowGraph, declaredNode.SyntaxTree, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); // Catch Exception from the start action. @@ -892,7 +982,7 @@ public void ExecuteOperationBlockActions( var (@this, startActions, declaredNode, operationBlocks, operations, ephemeralActions) = args; var context = new OperationBlockAnalysisContext(operationBlocks, executionData.DeclaredSymbol, @this.Compilation, - @this.AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, @this.GetControlFlowGraph, declaredNode.SyntaxTree, + executionData.AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, @this.GetControlFlowGraph, declaredNode.SyntaxTree, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); foreach (var blockAction in blockActions) @@ -948,7 +1038,8 @@ private void ExecuteBlockActionsCore( blockEndActions.AddAll(endActions); var diagReporter = GetAddSemanticDiagnostic( - executionData.SemanticModel.SyntaxTree, declaredNode.FullSpan, executionData.Analyzer, cancellationToken); + executionData.SemanticModel.SyntaxTree, declaredNode.FullSpan, + executionData.Analyzer, executionData.AnalyzerOptions, cancellationToken); // Include the stateful actions. foreach (var startAction in startActions) @@ -1012,7 +1103,9 @@ public void ExecuteSyntaxNodeActions( return; } - var diagReporter = GetAddSemanticDiagnostic(model.SyntaxTree, spanForContainingTopmostNodeForAnalysis, analyzer, cancellationToken); + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); + var diagReporter = GetAddSemanticDiagnostic( + model.SyntaxTree, spanForContainingTopmostNodeForAnalysis, analyzer, analyzerOptions, cancellationToken); using var _ = PooledDelegates.GetPooledFunction( static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), @@ -1021,7 +1114,7 @@ public void ExecuteSyntaxNodeActions( ExecuteSyntaxNodeActions( nodesToAnalyze, nodeActionsByKind, - new ExecutionData(analyzer, declaredSymbol, model, filterSpan, isGeneratedCode), + new ExecutionData(analyzer, analyzerOptions, declaredSymbol, model, filterSpan, isGeneratedCode), getKind, diagReporter, isSupportedDiagnostic, hasCodeBlockStartOrSymbolStartActions, cancellationToken); diagReporter.Free(); @@ -1111,7 +1204,9 @@ public void ExecuteOperationActions( return; } - var diagReporter = GetAddSemanticDiagnostic(model.SyntaxTree, spanForContainingOperationBlock, analyzer, cancellationToken); + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); + var diagReporter = GetAddSemanticDiagnostic( + model.SyntaxTree, spanForContainingOperationBlock, analyzer, analyzerOptions, cancellationToken); using var _ = PooledDelegates.GetPooledFunction( static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), @@ -1120,7 +1215,7 @@ public void ExecuteOperationActions( ExecuteOperationActions( operationsToAnalyze, operationActionsByKind, - new ExecutionData(analyzer, declaredSymbol, model, filterSpan, isGeneratedCode), + new ExecutionData(analyzer, analyzerOptions, declaredSymbol, model, filterSpan, isGeneratedCode), diagReporter, isSupportedDiagnostic, hasOperationBlockStartOrSymbolStartActions, cancellationToken); diagReporter.Free(); @@ -1412,10 +1507,26 @@ private bool IsSupportedDiagnostic(DiagnosticAnalyzer analyzer, Diagnostic diagn return _analyzerManager.IsSupportedDiagnostic(analyzer, diagnostic, _isCompilerAnalyzer, this, cancellationToken); } - private Action GetAddDiagnostic(ISymbol contextSymbol, ImmutableArray cachedDeclaringReferences, DiagnosticAnalyzer analyzer, Func getTopMostNodeForAnalysis, CancellationToken cancellationToken) + private Action GetAddDiagnostic( + ISymbol contextSymbol, + ImmutableArray cachedDeclaringReferences, + DiagnosticAnalyzer analyzer, + AnalyzerOptions options, + Func getTopMostNodeForAnalysis, + CancellationToken cancellationToken) { - return GetAddDiagnostic(contextSymbol, cachedDeclaringReferences, Compilation, analyzer, _addNonCategorizedDiagnostic, - _addCategorizedLocalDiagnostic, _addCategorizedNonLocalDiagnostic, getTopMostNodeForAnalysis, _shouldSuppressGeneratedCodeDiagnostic, cancellationToken); + return GetAddDiagnostic( + contextSymbol, + cachedDeclaringReferences, + Compilation, + analyzer, + options, + _addNonCategorizedDiagnostic, + _addCategorizedLocalDiagnostic, + _addCategorizedNonLocalDiagnostic, + getTopMostNodeForAnalysis, + _shouldSuppressGeneratedCodeDiagnostic, + cancellationToken); } private static Action GetAddDiagnostic( @@ -1423,9 +1534,10 @@ private static Action GetAddDiagnostic( ImmutableArray cachedDeclaringReferences, Compilation compilation, DiagnosticAnalyzer analyzer, - Action? addNonCategorizedDiagnostic, - Action? addCategorizedLocalDiagnostic, - Action? addCategorizedNonLocalDiagnostic, + AnalyzerOptions options, + Action? addNonCategorizedDiagnostic, + Action? addCategorizedLocalDiagnostic, + Action? addCategorizedNonLocalDiagnostic, Func getTopMostNodeForAnalysis, Func shouldSuppressGeneratedCodeDiagnostic, CancellationToken cancellationToken) @@ -1440,7 +1552,7 @@ private static Action GetAddDiagnostic( if (addCategorizedLocalDiagnostic == null) { Debug.Assert(addNonCategorizedDiagnostic != null); - addNonCategorizedDiagnostic(diagnostic, cancellationToken); + addNonCategorizedDiagnostic(diagnostic, options, cancellationToken); return; } @@ -1456,18 +1568,21 @@ private static Action GetAddDiagnostic( var syntax = getTopMostNodeForAnalysis(contextSymbol, syntaxRef, compilation, cancellationToken); if (diagnostic.Location.SourceSpan.IntersectsWith(syntax.FullSpan)) { - addCategorizedLocalDiagnostic(diagnostic, analyzer, false, cancellationToken); + addCategorizedLocalDiagnostic(diagnostic, analyzer, options, false, cancellationToken); return; } } } } - addCategorizedNonLocalDiagnostic(diagnostic, analyzer, cancellationToken); + addCategorizedNonLocalDiagnostic(diagnostic, analyzer, options, cancellationToken); }; } - private Action GetAddCompilationDiagnostic(DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) + private Action GetAddCompilationDiagnostic( + DiagnosticAnalyzer analyzer, + AnalyzerOptions analyzerOptions, + CancellationToken cancellationToken) { return diagnostic => { @@ -1479,31 +1594,47 @@ private Action GetAddCompilationDiagnostic(DiagnosticAnalyzer analyz if (_addCategorizedNonLocalDiagnostic == null) { Debug.Assert(_addNonCategorizedDiagnostic != null); - _addNonCategorizedDiagnostic(diagnostic, cancellationToken); + _addNonCategorizedDiagnostic(diagnostic, analyzerOptions, cancellationToken); return; } - _addCategorizedNonLocalDiagnostic(diagnostic, analyzer, cancellationToken); + _addCategorizedNonLocalDiagnostic(diagnostic, analyzer, analyzerOptions, cancellationToken); }; } - private AnalyzerDiagnosticReporter GetAddSemanticDiagnostic(SyntaxTree tree, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) + private AnalyzerDiagnosticReporter GetAddSemanticDiagnostic( + SyntaxTree tree, + DiagnosticAnalyzer analyzer, + AnalyzerOptions analyzerOptions, + CancellationToken cancellationToken) { - return AnalyzerDiagnosticReporter.GetInstance(new SourceOrAdditionalFile(tree), span: null, Compilation, analyzer, isSyntaxDiagnostic: false, + return AnalyzerDiagnosticReporter.GetInstance( + new SourceOrAdditionalFile(tree), span: null, Compilation, analyzer, analyzerOptions, isSyntaxDiagnostic: false, _addNonCategorizedDiagnostic, _addCategorizedLocalDiagnostic, _addCategorizedNonLocalDiagnostic, _shouldSuppressGeneratedCodeDiagnostic, cancellationToken); } - private AnalyzerDiagnosticReporter GetAddSemanticDiagnostic(SyntaxTree tree, TextSpan? span, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) + private AnalyzerDiagnosticReporter GetAddSemanticDiagnostic( + SyntaxTree tree, + TextSpan? span, + DiagnosticAnalyzer analyzer, + AnalyzerOptions analyzerOptions, + CancellationToken cancellationToken) { - return AnalyzerDiagnosticReporter.GetInstance(new SourceOrAdditionalFile(tree), span, Compilation, analyzer, isSyntaxDiagnostic: false, + return AnalyzerDiagnosticReporter.GetInstance( + new SourceOrAdditionalFile(tree), span, Compilation, analyzer, analyzerOptions, isSyntaxDiagnostic: false, _addNonCategorizedDiagnostic, _addCategorizedLocalDiagnostic, _addCategorizedNonLocalDiagnostic, _shouldSuppressGeneratedCodeDiagnostic, cancellationToken); } - private AnalyzerDiagnosticReporter GetAddSyntaxDiagnostic(SourceOrAdditionalFile file, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) + private AnalyzerDiagnosticReporter GetAddSyntaxDiagnostic( + SourceOrAdditionalFile file, + DiagnosticAnalyzer analyzer, + AnalyzerOptions analyzerOptions, + CancellationToken cancellationToken) { - return AnalyzerDiagnosticReporter.GetInstance(file, span: null, Compilation, analyzer, isSyntaxDiagnostic: true, + return AnalyzerDiagnosticReporter.GetInstance( + file, span: null, Compilation, analyzer, analyzerOptions, isSyntaxDiagnostic: true, _addNonCategorizedDiagnostic, _addCategorizedLocalDiagnostic, _addCategorizedNonLocalDiagnostic, _shouldSuppressGeneratedCodeDiagnostic, cancellationToken); } diff --git a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerOptions.cs b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerOptions.cs index 0b34cc33b77..a79219b0a37 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerOptions.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerOptions.cs @@ -63,6 +63,11 @@ public AnalyzerOptions WithAdditionalFiles(ImmutableArray additi return new AnalyzerOptions(additionalFiles); } + internal AnalyzerOptions WithAnalyzerConfigOptionsProvider(AnalyzerConfigOptionsProvider optionsProvider) + => this.AnalyzerConfigOptionsProvider == optionsProvider + ? this + : new(this.AdditionalFiles, optionsProvider); + public override bool Equals(object? obj) { if (ReferenceEquals(this, obj)) diff --git a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerOptionsExtensions.cs b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerOptionsExtensions.cs index e7b8ac7b8f7..62b28f20ac7 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerOptionsExtensions.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerOptionsExtensions.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.Collections.Immutable; using System.Threading; using Microsoft.CodeAnalysis.InternalUtilities; @@ -19,6 +20,9 @@ internal static class AnalyzerOptionsExtensions private static string GetCategoryBasedDotnetAnalyzerDiagnosticSeverityKey(string category) => s_categoryToSeverityKeyMap.GetOrAdd(category, category, static category => $"{DotnetAnalyzerDiagnosticPrefix}.{CategoryPrefix}-{category}.{SeveritySuffix}"); + public static ImmutableArray GetAdditionalFiles(this AnalyzerOptions? analyzerOptions) + => analyzerOptions?.AdditionalFiles ?? []; + /// /// Tries to get configured severity for the given /// for the given from bulk configuration analyzer config options, i.e. diff --git a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs index 67c47debe03..c3a5afae49e 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs @@ -104,7 +104,7 @@ public CompilationWithAnalyzers(Compilation compilation, ImmutableArray().ToImmutableArrayOrEmpty(); _analysisOptions = analysisOptions; - _analysisResultBuilder = new AnalysisResultBuilder(analysisOptions.LogAnalyzerExecutionTime, analyzers, _analysisOptions.Options?.AdditionalFiles ?? ImmutableArray.Empty); + _analysisResultBuilder = new AnalysisResultBuilder(analysisOptions.LogAnalyzerExecutionTime, analyzers, _analysisOptions.Options.GetAdditionalFiles()); _compilationAnalysisScope = AnalysisScope.Create(_compilation, _analyzers, this); } @@ -224,7 +224,7 @@ private void VerifyAdditionalFile(AdditionalText file) #endregion - private ImmutableArray AdditionalFiles => _analysisOptions.Options?.AdditionalFiles ?? ImmutableArray.Empty; + private ImmutableArray AdditionalFiles => _analysisOptions.Options.GetAdditionalFiles(); /// /// Returns diagnostics produced by all . diff --git a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzersOptions.cs b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzersOptions.cs index 015cf46f382..33cf088af40 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzersOptions.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzersOptions.cs @@ -48,6 +48,13 @@ public sealed class CompilationWithAnalyzersOptions /// public bool ReportSuppressedDiagnostics => _reportSuppressedDiagnostics; + /// + /// Callback to allow individual analyzers to have their own + /// distinct from the shared instance provided in . If then will be used for all analyzers. + /// + internal readonly Func? GetAnalyzerConfigOptionsProvider; + /// /// Creates a new . /// @@ -98,6 +105,23 @@ public CompilationWithAnalyzersOptions( bool logAnalyzerExecutionTime, bool reportSuppressedDiagnostics, Func? analyzerExceptionFilter) + : this(options, onAnalyzerException, concurrentAnalysis, logAnalyzerExecutionTime, reportSuppressedDiagnostics, analyzerExceptionFilter, getAnalyzerConfigOptionsProvider: null) + { + } + + /// + /// Callback to allow individual analyzers to have their own distinct from the shared instance provided in . If then will be used for all + /// analyzers. + public CompilationWithAnalyzersOptions( + AnalyzerOptions? options, + Action? onAnalyzerException, + bool concurrentAnalysis, + bool logAnalyzerExecutionTime, + bool reportSuppressedDiagnostics, + Func? analyzerExceptionFilter, + Func? getAnalyzerConfigOptionsProvider) { _options = options; _onAnalyzerException = onAnalyzerException; @@ -105,6 +129,7 @@ public CompilationWithAnalyzersOptions( _concurrentAnalysis = concurrentAnalysis; _logAnalyzerExecutionTime = logAnalyzerExecutionTime; _reportSuppressedDiagnostics = reportSuppressedDiagnostics; + this.GetAnalyzerConfigOptionsProvider = getAnalyzerConfigOptionsProvider; } } } diff --git a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs index db530e231f9..9fa5b256030 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs @@ -215,6 +215,11 @@ internal override bool TryGetValueCore(TKey key, AnalysisValueProv var compilationAnalysisValueProvider = _compilationAnalysisValueProviderFactory.GetValueProvider(valueProvider); return compilationAnalysisValueProvider.TryGetValue(key, out value); } + + public AnalyzerCompilationStartAnalysisContext WithOptions(AnalyzerOptions options) + => this.Options == options + ? this + : new(_scope, this.Compilation, options, _compilationAnalysisValueProviderFactory, this.CancellationToken); } /// @@ -279,6 +284,11 @@ public override void RegisterOperationAction(Action ac DiagnosticAnalysisContextHelpers.VerifyArguments(action, operationKinds); _scope.RegisterOperationAction(action, operationKinds); } + + public AnalyzerSymbolStartAnalysisContext WithOptions(AnalyzerOptions analyzerOptions) + => this.Options == analyzerOptions + ? this + : new(_scope, this.Symbol, this.Compilation, analyzerOptions, this.IsGeneratedCode, this.FilterTree, this.FilterSpan, this.CancellationToken); } /// diff --git a/src/roslyn/src/Compilers/Core/Portable/InternalUtilities/PlatformInformation.cs b/src/roslyn/src/Compilers/Core/Portable/InternalUtilities/PlatformInformation.cs index 559fc24c63a..84803050a94 100644 --- a/src/roslyn/src/Compilers/Core/Portable/InternalUtilities/PlatformInformation.cs +++ b/src/roslyn/src/Compilers/Core/Portable/InternalUtilities/PlatformInformation.cs @@ -57,5 +57,7 @@ public static bool IsUsingMonoRuntime } } } + + public static string ExeExtension => IsWindows ? ".exe" : string.Empty; } } diff --git a/src/roslyn/src/Compilers/Core/Portable/InternalUtilities/StringExtensions.cs b/src/roslyn/src/Compilers/Core/Portable/InternalUtilities/StringExtensions.cs index c727e4c7da1..4e141e9d807 100644 --- a/src/roslyn/src/Compilers/Core/Portable/InternalUtilities/StringExtensions.cs +++ b/src/roslyn/src/Compilers/Core/Portable/InternalUtilities/StringExtensions.cs @@ -138,20 +138,20 @@ internal static bool TryGetWithoutAttributeSuffix( } internal static string? GetWithoutAttributeSuffix( - this string name, + this string? name, bool isCaseSensitive) { return TryGetWithoutAttributeSuffix(name, isCaseSensitive, out var result) ? result : null; } internal static bool TryGetWithoutAttributeSuffix( - this string name, + this string? name, bool isCaseSensitive, [NotNullWhen(returnValue: true)] out string? result) { if (name.HasAttributeSuffix(isCaseSensitive)) { - result = name.Substring(0, name.Length - AttributeSuffix.Length); + result = name[..^AttributeSuffix.Length]; return true; } @@ -159,8 +159,11 @@ internal static bool TryGetWithoutAttributeSuffix( return false; } - internal static bool HasAttributeSuffix(this string name, bool isCaseSensitive) + internal static bool HasAttributeSuffix([NotNullWhen(true)] this string? name, bool isCaseSensitive) { + if (name is null) + return false; + var comparison = isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; return name.Length > AttributeSuffix.Length && name.EndsWith(AttributeSuffix, comparison); } diff --git a/src/roslyn/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/roslyn/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 3dd2e036998..21575cf0f0b 100644 --- a/src/roslyn/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/roslyn/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -8,11 +8,15 @@ Microsoft.CodeAnalysis.CommandLineResource.IsPublic.get -> bool Microsoft.CodeAnalysis.CommandLineResource.LinkedResourceFileName.get -> string? Microsoft.CodeAnalysis.CommandLineResource.ResourceName.get -> string! Microsoft.CodeAnalysis.Compilation.EmitDifference(Microsoft.CodeAnalysis.Emit.EmitBaseline! baseline, System.Collections.Generic.IEnumerable! edits, System.Func! isAddedSymbol, System.IO.Stream! metadataStream, System.IO.Stream! ilStream, System.IO.Stream! pdbStream, Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.Emit.EmitDifferenceResult! +Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions.CompilationWithAnalyzersOptions(Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions? options, System.Action? onAnalyzerException, bool concurrentAnalysis, bool logAnalyzerExecutionTime, bool reportSuppressedDiagnostics, System.Func? analyzerExceptionFilter, System.Func? getAnalyzerConfigOptionsProvider) -> void Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions.EmitDifferenceOptions() -> void Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions.EmitFieldRva.get -> bool Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions.EmitFieldRva.init -> void +Microsoft.CodeAnalysis.IMethodSymbol.AssociatedExtensionImplementation.get -> Microsoft.CodeAnalysis.IMethodSymbol? Microsoft.CodeAnalysis.IMethodSymbol.IsIterator.get -> bool +Microsoft.CodeAnalysis.INamedTypeSymbol.ExtensionParameter.get -> Microsoft.CodeAnalysis.IParameterSymbol? +Microsoft.CodeAnalysis.INamedTypeSymbol.IsExtension.get -> bool static readonly Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions.Default -> Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions Microsoft.CodeAnalysis.IEventSymbol.IsPartialDefinition.get -> bool Microsoft.CodeAnalysis.IEventSymbol.PartialDefinitionPart.get -> Microsoft.CodeAnalysis.IEventSymbol? diff --git a/src/roslyn/src/Compilers/Core/Portable/Symbols/IFieldSymbol.cs b/src/roslyn/src/Compilers/Core/Portable/Symbols/IFieldSymbol.cs index 2732bf20659..f42ba960ae6 100644 --- a/src/roslyn/src/Compilers/Core/Portable/Symbols/IFieldSymbol.cs +++ b/src/roslyn/src/Compilers/Core/Portable/Symbols/IFieldSymbol.cs @@ -79,6 +79,11 @@ public interface IFieldSymbol : ISymbol /// /// Gets the top-level nullability of this field. /// + /// + /// The inferred nullability of a backing field symbol (for example, the symbol for a keyword) is not exposed by this API. + /// In that case, this API returns the nullable annotation of the associated property. + /// The inferred nullability is only exposed indirectly via APIs that give information about expressions, such as when called with a expression. + /// NullableAnnotation NullableAnnotation { get; } /// diff --git a/src/roslyn/src/Compilers/Core/Portable/Symbols/IMethodSymbol.cs b/src/roslyn/src/Compilers/Core/Portable/Symbols/IMethodSymbol.cs index 4a46d267649..6791aabca03 100644 --- a/src/roslyn/src/Compilers/Core/Portable/Symbols/IMethodSymbol.cs +++ b/src/roslyn/src/Compilers/Core/Portable/Symbols/IMethodSymbol.cs @@ -43,7 +43,7 @@ public interface IMethodSymbol : ISymbol /// /// Returns false for methods in extension() blocks. /// To check if a method is a "new" extension method (a member of an extension() block), - /// check on the method's . + /// check on the method's . /// bool IsExtensionMethod { get; } @@ -304,5 +304,25 @@ public interface IMethodSymbol : ISymbol /// Returns if this method is a source method implemented as an iterator (either sync or async) /// bool IsIterator { get; } + + // Tracked by https://github.com/dotnet/roslyn/issues/78957 : public API, add support for constructed symbols + /// + /// For a method/accessor/operator in an extension block, returns the corresponding implementation method if one exists. + /// Returns null otherwise. + /// + /// For example, considering: + /// + /// static class E + /// { + /// extension(int i) + /// { + /// public void M() { } + /// } + /// } + /// + /// When given the method symbol for E.extension(int i).M(), + /// it will return the corresponding static implementation method E.M(this int i). + /// + IMethodSymbol? AssociatedExtensionImplementation { get; } } } diff --git a/src/roslyn/src/Compilers/Core/Portable/Symbols/INamedTypeSymbol.cs b/src/roslyn/src/Compilers/Core/Portable/Symbols/INamedTypeSymbol.cs index a5a2d685d6d..577bb9b8f46 100644 --- a/src/roslyn/src/Compilers/Core/Portable/Symbols/INamedTypeSymbol.cs +++ b/src/roslyn/src/Compilers/Core/Portable/Symbols/INamedTypeSymbol.cs @@ -196,5 +196,16 @@ public interface INamedTypeSymbol : ITypeSymbol /// Otherwise, returns null. /// INamedTypeSymbol? NativeIntegerUnderlyingType { get; } + + /// + /// Is this a symbol for an extension declaration. + /// + new bool IsExtension { get; } + + /// + /// The extension parameter if this is an extension declaration ( is true). + /// Note: this may be null even if is true, in error cases. + /// + new IParameterSymbol? ExtensionParameter { get; } } } diff --git a/src/roslyn/src/Compilers/Core/Portable/Symbols/ITypeSymbol.cs b/src/roslyn/src/Compilers/Core/Portable/Symbols/ITypeSymbol.cs index 176ba1cfe07..85bb9c9c39d 100644 --- a/src/roslyn/src/Compilers/Core/Portable/Symbols/ITypeSymbol.cs +++ b/src/roslyn/src/Compilers/Core/Portable/Symbols/ITypeSymbol.cs @@ -81,15 +81,10 @@ public interface ITypeSymbol : INamespaceOrTypeSymbol /// bool IsNativeIntegerType { get; } - /// - /// Is this a symbol for an extension declaration. - /// + [Obsolete($"This API will be removed in the future. Use {nameof(INamedTypeSymbol)}.{nameof(INamedTypeSymbol.IsExtension)} instead.")] bool IsExtension { get; } - /// - /// The extension parameter if this is an extension declaration ( is true). - /// Note: this may be null even if is true, in error cases. - /// + [Obsolete($"This API will be removed in the future. Use {nameof(INamedTypeSymbol)}.{nameof(INamedTypeSymbol.ExtensionParameter)} instead.")] IParameterSymbol? ExtensionParameter { get; } /// diff --git a/src/roslyn/src/Compilers/Core/SdkTaskTests/Microsoft.Build.Tasks.CodeAnalysis.Sdk.UnitTests.csproj b/src/roslyn/src/Compilers/Core/SdkTaskTests/Microsoft.Build.Tasks.CodeAnalysis.Sdk.UnitTests.csproj index 12e7ee7325c..e0ff037f975 100644 --- a/src/roslyn/src/Compilers/Core/SdkTaskTests/Microsoft.Build.Tasks.CodeAnalysis.Sdk.UnitTests.csproj +++ b/src/roslyn/src/Compilers/Core/SdkTaskTests/Microsoft.Build.Tasks.CodeAnalysis.Sdk.UnitTests.csproj @@ -58,5 +58,6 @@ + diff --git a/src/roslyn/src/Compilers/Core/SdkTaskTests/SdkManagedToolTests.cs b/src/roslyn/src/Compilers/Core/SdkTaskTests/SdkManagedToolTests.cs index 9c235ed8f8f..1c86e743c7d 100644 --- a/src/roslyn/src/Compilers/Core/SdkTaskTests/SdkManagedToolTests.cs +++ b/src/roslyn/src/Compilers/Core/SdkTaskTests/SdkManagedToolTests.cs @@ -2,12 +2,9 @@ // 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 Roslyn.Test.Utilities; -using Microsoft.CodeAnalysis.Test.Utilities; -using Xunit.Abstractions; using Xunit; +using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.BuildTasks.Sdk.UnitTests; @@ -25,7 +22,12 @@ public void PathToBuiltinTool() { var taskPath = Path.GetDirectoryName(typeof(ManagedCompiler).Assembly.Location)!; var task = new Csc(); - Assert.Equal(Path.Combine(taskPath, "..", "bincore", "csc.dll"), task.PathToBuiltInTool); + Assert.Contains(task.PathToBuiltInTool, + new[] + { + Path.Combine(taskPath, "..", "bincore", "csc.dll"), + Path.Combine(taskPath, "..", "bincore", "csc.exe"), + }); } [Fact] diff --git a/src/roslyn/src/Compilers/Server/VBCSCompiler/AnyCpu/VBCSCompiler.csproj b/src/roslyn/src/Compilers/Server/VBCSCompiler/AnyCpu/VBCSCompiler.csproj index ab0107e8807..63820995a72 100644 --- a/src/roslyn/src/Compilers/Server/VBCSCompiler/AnyCpu/VBCSCompiler.csproj +++ b/src/roslyn/src/Compilers/Server/VBCSCompiler/AnyCpu/VBCSCompiler.csproj @@ -4,8 +4,8 @@ Exe $(NetRoslynSourceBuild);net472 - false true + false diff --git a/src/roslyn/src/Compilers/Server/VBCSCompilerTests/CompilerServerApiTest.cs b/src/roslyn/src/Compilers/Server/VBCSCompilerTests/CompilerServerApiTest.cs index 4d111d88a99..d8f7a830b2a 100644 --- a/src/roslyn/src/Compilers/Server/VBCSCompilerTests/CompilerServerApiTest.cs +++ b/src/roslyn/src/Compilers/Server/VBCSCompilerTests/CompilerServerApiTest.cs @@ -130,9 +130,8 @@ public async Task IncorrectServerHashReturnsIncorrectHashResponse() public void QuotePipeName_Desktop() { var serverInfo = BuildServerConnection.GetServerProcessInfo(@"q:\tools", "name with space"); - // Because q:\tools\VBCSCompiler.exe does not exist, the fallback 'dotnet exec VBCSCompiler.dll' is returned. Assert.EndsWith(@"\dotnet.exe", serverInfo.processFilePath); - Assert.Equal(@"exec ""q:\tools\VBCSCompiler.dll"" ""-pipename:name with space""", serverInfo.commandLineArguments); + AssertEx.Equal(@"exec ""q:\tools\VBCSCompiler.dll"" ""-pipename:name with space""", serverInfo.commandLineArguments); } [ConditionalFact(typeof(CoreClrOnly))] @@ -144,7 +143,7 @@ public void QuotePipeName_CoreClr() : "/tools"; var serverInfo = BuildServerConnection.GetServerProcessInfo(toolDir, "name with space"); var vbcsFilePath = Path.Combine(toolDir, "VBCSCompiler.dll"); - Assert.Equal($@"exec ""{vbcsFilePath}"" ""-pipename:name with space""", serverInfo.commandLineArguments); + AssertEx.Equal($@"exec ""{vbcsFilePath}"" ""-pipename:name with space""", serverInfo.commandLineArguments); } [Theory] diff --git a/src/roslyn/src/Compilers/Shared/BuildServerConnection.cs b/src/roslyn/src/Compilers/Shared/BuildServerConnection.cs index 732627d6096..c968fec249d 100644 --- a/src/roslyn/src/Compilers/Shared/BuildServerConnection.cs +++ b/src/roslyn/src/Compilers/Shared/BuildServerConnection.cs @@ -432,11 +432,12 @@ internal static async Task MonitorDisconnectAsync( internal static (string processFilePath, string commandLineArguments) GetServerProcessInfo(string clientDir, string pipeName) { - var processFilePath = Path.Combine(clientDir, "VBCSCompiler.exe"); + var processFilePath = Path.Combine(clientDir, $"VBCSCompiler{PlatformInformation.ExeExtension}"); var commandLineArgs = $@"""-pipename:{pipeName}"""; + if (!File.Exists(processFilePath)) { - // This is a .NET Core deployment + // Fallback to not use the apphost if it is not present (can happen in compiler toolset scenarios for example). commandLineArgs = RuntimeHostInfo.GetDotNetExecCommandLine(Path.ChangeExtension(processFilePath, ".dll"), commandLineArgs); processFilePath = RuntimeHostInfo.GetDotNetPathOrDefault(); } @@ -462,81 +463,95 @@ internal static bool TryCreateServer(string clientDirectory, string pipeName, IC logger.Log("Attempting to create process '{0}' {1}", serverInfo.processFilePath, serverInfo.commandLineArguments); - if (PlatformInformation.IsWindows) + string? previousDotNetRoot = Environment.GetEnvironmentVariable(RuntimeHostInfo.DotNetRootEnvironmentName); + if (RuntimeHostInfo.GetToolDotNetRoot() is { } dotNetRoot) { - // As far as I can tell, there isn't a way to use the Process class to - // create a process with no stdin/stdout/stderr, so we use P/Invoke. - // This code was taken from MSBuild task starting code. - - STARTUPINFO startInfo = new STARTUPINFO(); - startInfo.cb = Marshal.SizeOf(startInfo); - startInfo.hStdError = InvalidIntPtr; - startInfo.hStdInput = InvalidIntPtr; - startInfo.hStdOutput = InvalidIntPtr; - startInfo.dwFlags = STARTF_USESTDHANDLES; - uint dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW; - - PROCESS_INFORMATION processInfo; - - var builder = new StringBuilder($@"""{serverInfo.processFilePath}"" {serverInfo.commandLineArguments}"); - - bool success = CreateProcess( - lpApplicationName: null, - lpCommandLine: builder, - lpProcessAttributes: NullPtr, - lpThreadAttributes: NullPtr, - bInheritHandles: false, - dwCreationFlags: dwCreationFlags, - lpEnvironment: NullPtr, // Inherit environment - lpCurrentDirectory: clientDirectory, - lpStartupInfo: ref startInfo, - lpProcessInformation: out processInfo); - - if (success) - { - logger.Log("Successfully created process with process id {0}", processInfo.dwProcessId); - CloseHandle(processInfo.hProcess); - CloseHandle(processInfo.hThread); - processId = processInfo.dwProcessId; - } - else - { - logger.LogError("Failed to create process. GetLastError={0}", Marshal.GetLastWin32Error()); - } - return success; + logger.Log("Setting {0} to '{1}'", RuntimeHostInfo.DotNetRootEnvironmentName, dotNetRoot); + Environment.SetEnvironmentVariable(RuntimeHostInfo.DotNetRootEnvironmentName, dotNetRoot); } - else + + try { - try + if (PlatformInformation.IsWindows) { - var startInfo = new ProcessStartInfo() - { - FileName = serverInfo.processFilePath, - Arguments = serverInfo.commandLineArguments, - UseShellExecute = false, - WorkingDirectory = clientDirectory, - RedirectStandardInput = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; - - if (Process.Start(startInfo) is { } process) + // As far as I can tell, there isn't a way to use the Process class to + // create a process with no stdin/stdout/stderr, so we use P/Invoke. + // This code was taken from MSBuild task starting code. + + STARTUPINFO startInfo = new STARTUPINFO(); + startInfo.cb = Marshal.SizeOf(startInfo); + startInfo.hStdError = InvalidIntPtr; + startInfo.hStdInput = InvalidIntPtr; + startInfo.hStdOutput = InvalidIntPtr; + startInfo.dwFlags = STARTF_USESTDHANDLES; + uint dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW; + + PROCESS_INFORMATION processInfo; + + var builder = new StringBuilder($@"""{serverInfo.processFilePath}"" {serverInfo.commandLineArguments}"); + + bool success = CreateProcess( + lpApplicationName: null, + lpCommandLine: builder, + lpProcessAttributes: NullPtr, + lpThreadAttributes: NullPtr, + bInheritHandles: false, + dwCreationFlags: dwCreationFlags, + lpEnvironment: NullPtr, // Inherit environment + lpCurrentDirectory: clientDirectory, + lpStartupInfo: ref startInfo, + lpProcessInformation: out processInfo); + + if (success) { - processId = process.Id; - logger.Log("Successfully created process with process id {0}", processId); - return true; + logger.Log("Successfully created process with process id {0}", processInfo.dwProcessId); + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); + processId = processInfo.dwProcessId; } else { - return false; + logger.LogError("Failed to create process. GetLastError={0}", Marshal.GetLastWin32Error()); } + return success; } - catch + else { - return false; + try + { + var startInfo = new ProcessStartInfo() + { + FileName = serverInfo.processFilePath, + Arguments = serverInfo.commandLineArguments, + UseShellExecute = false, + WorkingDirectory = clientDirectory, + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true + }; + + if (Process.Start(startInfo) is { } process) + { + processId = process.Id; + logger.Log("Successfully created process with process id {0}", processId); + return true; + } + else + { + return false; + } + } + catch + { + return false; + } } } + finally + { + Environment.SetEnvironmentVariable(RuntimeHostInfo.DotNetRootEnvironmentName, previousDotNetRoot); + } } /// diff --git a/src/roslyn/src/Compilers/Shared/RuntimeHostInfo.cs b/src/roslyn/src/Compilers/Shared/RuntimeHostInfo.cs index 7ff9b16c7df..a77574e59cd 100644 --- a/src/roslyn/src/Compilers/Shared/RuntimeHostInfo.cs +++ b/src/roslyn/src/Compilers/Shared/RuntimeHostInfo.cs @@ -5,10 +5,7 @@ #nullable enable using System; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.IO; -using System.IO.Pipes; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -21,9 +18,6 @@ internal static class RuntimeHostInfo { internal static bool IsDesktopRuntime => !IsCoreClrRuntime; - internal static string GetDotNetExecCommandLine(string toolFilePath, string commandLineArguments) => - $@"exec ""{toolFilePath}"" {commandLineArguments}"; - internal static bool IsCoreClrRuntime => #if NET true; @@ -31,15 +25,24 @@ internal static string GetDotNetExecCommandLine(string toolFilePath, string comm false; #endif + internal const string DotNetRootEnvironmentName = "DOTNET_ROOT"; private const string DotNetHostPathEnvironmentName = "DOTNET_HOST_PATH"; private const string DotNetExperimentalHostPathEnvironmentName = "DOTNET_EXPERIMENTAL_HOST_PATH"; /// - /// Get the path to the dotnet executable. In the case the .NET SDK did not provide this information - /// in the environment this tries to find "dotnet" on the PATH. In the case it is not found, - /// this will return simply "dotnet". + /// The DOTNET_ROOT that should be used when launching executable tools. /// - internal static string GetDotNetPathOrDefault() + internal static string? GetToolDotNetRoot() + { + if (GetDotNetHostPath() is { } dotNetHostPath) + { + return Path.GetDirectoryName(dotNetHostPath); + } + + return null; + } + + private static string? GetDotNetHostPath() { if (Environment.GetEnvironmentVariable(DotNetHostPathEnvironmentName) is { Length: > 0 } pathToDotNet) { @@ -51,6 +54,21 @@ internal static string GetDotNetPathOrDefault() return pathToDotNetExperimental; } + return null; + } + + /// + /// Get the path to the dotnet executable. In the case the .NET SDK did not provide this information + /// in the environment this tries to find "dotnet" on the PATH. In the case it is not found, + /// this will return simply "dotnet". + /// + internal static string GetDotNetPathOrDefault() + { + if (GetDotNetHostPath() is { } pathToDotNet) + { + return pathToDotNet; + } + var (fileName, sep) = PlatformInformation.IsWindows ? ("dotnet.exe", new char[] { ';' }) : ("dotnet", new char[] { ':' }); @@ -74,5 +92,8 @@ internal static string GetDotNetPathOrDefault() return fileName; } + + internal static string GetDotNetExecCommandLine(string toolFilePath, string commandLineArguments) => + $@"exec ""{toolFilePath}"" {commandLineArguments}"; } } diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/roslyn/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index 46e90c7caa5..47ff89d6a4b 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -943,7 +943,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic For Each tree As SyntaxTree In trees If tree Is Nothing Then - Throw New ArgumentNullException(String.Format(VBResources.Trees0, i)) + Throw New ArgumentNullException($"trees({i})") End If If Not tree.HasCompilationUnitRoot Then @@ -955,7 +955,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End If If declMap.ContainsKey(tree) Then - Throw New ArgumentException(VBResources.SyntaxTreeAlreadyPresent, String.Format(VBResources.Trees0, i)) + Throw New ArgumentException(VBResources.SyntaxTreeAlreadyPresent, $"trees({i})") End If AddSyntaxTreeToDeclarationMapAndTable(tree, _options, Me.IsSubmission, declMap, declTable, referenceDirectivesChanged) ' declMap and declTable passed ByRef diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/MethodSymbol.vb b/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/MethodSymbol.vb index 7acc4a1a228..c85130b736d 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/MethodSymbol.vb +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/MethodSymbol.vb @@ -1236,6 +1236,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Return visitor.VisitMethod(Me) End Function + Public ReadOnly Property AssociatedExtensionImplementation As IMethodSymbol Implements IMethodSymbol.AssociatedExtensionImplementation + Get + Return Nothing + End Get + End Property + #End Region End Class diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb b/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb index dc4e5306965..411f601a603 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb @@ -1569,5 +1569,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Return False End Function + Public ReadOnly Property IsExtension As Boolean Implements INamedTypeSymbol.IsExtension + Get + Return False + End Get + End Property + + Public ReadOnly Property ExtensionParameter As IParameterSymbol Implements INamedTypeSymbol.ExtensionParameter + Get + Return Nothing + End Get + End Property End Class End Namespace diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb b/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb index 18b9b207233..be406d80fcd 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb @@ -783,13 +783,15 @@ Done: Return Me End Function - Public ReadOnly Property IsExtension As Boolean Implements ITypeSymbol.IsExtension + + Private ReadOnly Property ITypeSymbol_IsExtension As Boolean Implements ITypeSymbol.IsExtension Get Return False End Get End Property - Public ReadOnly Property ExtensionParameter As IParameterSymbol Implements ITypeSymbol.ExtensionParameter + + Private ReadOnly Property ITypeSymbol_ExtensionParameter As IParameterSymbol Implements ITypeSymbol.ExtensionParameter Get Return Nothing End Get diff --git a/src/roslyn/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb b/src/roslyn/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb index de3ea903da8..46eb5463fb7 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb +++ b/src/roslyn/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb @@ -6066,7 +6066,7 @@ static class E End If Dim e = DirectCast(comp.GlobalNamespace.GetMembers("E").Single(), ITypeSymbol) - Dim extension = e.GetMembers().OfType(Of ITypeSymbol).Single() + Dim extension = e.GetMembers().OfType(Of INamedTypeSymbol).Single() Assert.True(extension.IsExtension) AssertEx.Equal("E.$119AA281C143547563250CAF89B48A76", SymbolDisplay.ToDisplayString(extension, format)) @@ -6121,7 +6121,7 @@ static class E End If Dim e = DirectCast(comp.GlobalNamespace.GetMembers("E").Single(), ITypeSymbol) - Dim extension = e.GetMembers().OfType(Of ITypeSymbol).Single() + Dim extension = e.GetMembers().OfType(Of INamedTypeSymbol).Single() ' Tracked by https://github.com/dotnet/roslyn/issues/78957 : public API, the arity should not be included in the extension type name Assert.True(extension.IsExtension) diff --git a/src/roslyn/src/Compilers/VisualBasic/vbc/AnyCpu/vbc.csproj b/src/roslyn/src/Compilers/VisualBasic/vbc/AnyCpu/vbc.csproj index f4abf997885..7a2f3558bbf 100644 --- a/src/roslyn/src/Compilers/VisualBasic/vbc/AnyCpu/vbc.csproj +++ b/src/roslyn/src/Compilers/VisualBasic/vbc/AnyCpu/vbc.csproj @@ -4,8 +4,8 @@ Exe $(NetRoslynSourceBuild);net472 - false true + false diff --git a/src/roslyn/src/EditorFeatures/CSharp/EventHookup/EventHookupCommandHandler_TabKeyCommand.cs b/src/roslyn/src/EditorFeatures/CSharp/EventHookup/EventHookupCommandHandler_TabKeyCommand.cs index c84d5a54ffc..1ad65b2df0d 100644 --- a/src/roslyn/src/EditorFeatures/CSharp/EventHookup/EventHookupCommandHandler_TabKeyCommand.cs +++ b/src/roslyn/src/EditorFeatures/CSharp/EventHookup/EventHookupCommandHandler_TabKeyCommand.cs @@ -144,7 +144,7 @@ async Task TryExecuteCommandAsync() { _threadingContext.ThrowIfNotOnUIThread(); - var factory = document.Project.Solution.Workspace.Services.GetRequiredService(); + var factory = document.Project.Solution.Services.GetRequiredService(); using var waitContext = factory.Create( textView, applicableToSpan, diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests_Regex.cs b/src/roslyn/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests_Regex.cs index 0234b4b8bb6..e1acae43702 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests_Regex.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests_Regex.cs @@ -1290,4 +1290,32 @@ void Bar([StringSyntax(StringSyntaxAttribute.Regex)] string p) } """ + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, testHost); + + [Theory, CombinatorialData] + [WorkItem("https://github.com/dotnet/roslyn/issues/80179")] + public Task TestRegexOnApiWithStringSyntaxAttribute_ParamsReadOnlyCollectionArgument(TestHost testHost) + => TestAsync( + """ + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Text.RegularExpressions; + + class Program + { + private void M([StringSyntax(StringSyntaxAttribute.Regex)] params IReadOnlyCollection p) + { + } + + void Goo() + { + [|M([@"$\a(?#comment)"]);|] + } + } + """ + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, + testHost, + Method("M"), + Regex.Anchor("$"), + Regex.OtherEscape("\\"), + Regex.OtherEscape("a"), + Regex.Comment("(?#comment)")); } diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/AddUsing/AddUsingTests_ExtensionMethods.cs b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/AddUsing/AddUsingTests_ExtensionMethods.cs index 49ebd1d6c6e..8809da750dd 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/AddUsing/AddUsingTests_ExtensionMethods.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/AddUsing/AddUsingTests_ExtensionMethods.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.Threading.Tasks; using Microsoft.CodeAnalysis.Remote.Testing; using Roslyn.Test.Utilities; @@ -1508,4 +1506,64 @@ public static T Bar( this Goo @this ) } } """); + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/80240")] + [InlineData("object")] + [InlineData("int")] + public Task TestModernExtension1(string sourceType) + => TestInRegularAndScriptAsync( + $$""" + namespace M + { + class C + { + void X() + { + {{sourceType}} o = new(); + [|o.M1();|] + } + } + } + + namespace N + { + static class Test + { + extension(object o) + { + public void M1() + { + } + } + } + } + """, + $$""" + using N; + + namespace M + { + class C + { + void X() + { + {{sourceType}} o = new(); + o.M1(); + } + } + } + + namespace N + { + static class Test + { + extension(object o) + { + public void M1() + { + } + } + } + } + """); } diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs index d9bd2d2acc4..bb5b2208f17 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs @@ -52,7 +52,7 @@ protected override Task CreateWorkspaceImplAsync() } private static OptionsCollection PreferImplicitTypeWithInfo() - => new OptionsCollection(LanguageNames.CSharp) + => new(LanguageNames.CSharp) { { CSharpCodeStyleOptions.VarElsewhere, true, NotificationOption2.Suggestion }, { CSharpCodeStyleOptions.VarWhenTypeIsApparent, true, NotificationOption2.Suggestion }, @@ -60,7 +60,7 @@ private static OptionsCollection PreferImplicitTypeWithInfo() }; private static OptionsCollection PreferExplicitTypeWithInfo() - => new OptionsCollection(LanguageNames.CSharp) + => new(LanguageNames.CSharp) { { CSharpCodeStyleOptions.VarElsewhere, false, NotificationOption2.Suggestion }, { CSharpCodeStyleOptions.VarWhenTypeIsApparent, false, NotificationOption2.Suggestion }, diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/InitializeMemberFromParameterTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/InitializeMemberFromParameterTests.cs index ad2dab6115a..02cec1878e0 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/InitializeMemberFromParameterTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/InitializeMemberFromParameterTests.cs @@ -1454,9 +1454,9 @@ public C([|string p__End, string p_test_t|]) } """, parameters: new TestParameters(options: options.MergeStyles(options.PropertyNamesArePascalCase, options.ParameterNamesAreCamelCaseWithPUnderscorePrefixAndUnderscoreEndSuffix))); - private TestParameters OmitIfDefault_Warning => new TestParameters(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.OmitIfDefault, NotificationOption2.Warning)); - private TestParameters Never_Warning => new TestParameters(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.Never, NotificationOption2.Warning)); - private TestParameters Always_Warning => new TestParameters(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.Always, NotificationOption2.Warning)); + private TestParameters OmitIfDefault_Warning => new(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.OmitIfDefault, NotificationOption2.Warning)); + private TestParameters Never_Warning => new(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.Never, NotificationOption2.Warning)); + private TestParameters Always_Warning => new(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.Always, NotificationOption2.Warning)); [Fact] public Task TestCreateFieldWithTopLevelNullability() diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/InitializeMemberFromPrimaryConstructorParameterTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/InitializeMemberFromPrimaryConstructorParameterTests.cs index c23727189c2..f16e876670f 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/InitializeMemberFromPrimaryConstructorParameterTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/InitializeMemberFromPrimaryConstructorParameterTests.cs @@ -23,9 +23,9 @@ protected override CodeRefactoringProvider CreateCodeRefactoringProvider(EditorT private readonly NamingStylesTestOptionSets _options = new(LanguageNames.CSharp); - private TestParameters OmitIfDefault_Warning => new TestParameters(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.OmitIfDefault, NotificationOption2.Warning)); - private TestParameters Never_Warning => new TestParameters(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.Never, NotificationOption2.Warning)); - private TestParameters Always_Warning => new TestParameters(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.Always, NotificationOption2.Warning)); + private TestParameters OmitIfDefault_Warning => new(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.OmitIfDefault, NotificationOption2.Warning)); + private TestParameters Never_Warning => new(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.Never, NotificationOption2.Warning)); + private TestParameters Always_Warning => new(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.Always, NotificationOption2.Warning)); [Fact] public Task TestInitializeFieldWithSameName() diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/MoveStaticMembers/CSharpMoveStaticMembersTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/MoveStaticMembers/CSharpMoveStaticMembersTests.cs index d943b9e2b3c..7ec4b930146 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/MoveStaticMembers/CSharpMoveStaticMembersTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/MoveStaticMembers/CSharpMoveStaticMembersTests.cs @@ -792,6 +792,53 @@ public static int TestMethod() """, "Class1Helpers.cs", selectedMembers, "Class1Helpers").ConfigureAwait(false); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79806")] + public async Task TestMoveStaticMethodWithStaticMembers() + { + var selectedMembers = ImmutableArray.Create("StaticMethod"); + + await TestMovementNewFileAsync(""" + using System; + + namespace TestNs1 + { + internal class ClassWithStaticMembers + { + public static int StaticInt { get; set; } + public static string StaticString { get; set; } + public static void Static[||]Method() + { + Console.WriteLine(StaticString + StaticInt); + } + } + } + """, """ + using System; + + namespace TestNs1 + { + internal class ClassWithStaticMembers + { + public static int StaticInt { get; set; } + public static string StaticString { get; set; } + } + } + """, """ + using System; + + namespace TestNs1 + { + internal static class ClassWithStaticMembersHelpers + { + public static void StaticMethod() + { + Console.WriteLine(ClassWithStaticMembers.StaticString + ClassWithStaticMembers.StaticInt); + } + } + } + """, "ClassWithStaticMembersHelpers.cs", selectedMembers, "ClassWithStaticMembersHelpers").ConfigureAwait(false); + } + [Fact] public async Task TestMoveMethodAndRefactorUsageWithTrivia() { diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/PreviewExceptionTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/PreviewExceptionTests.cs index 7a9ced1875e..8787655b88b 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/PreviewExceptionTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/PreviewExceptionTests.cs @@ -87,14 +87,14 @@ private static async Task ActionSets(EditorTestWorkspace workspace, CodeRefactor Assert.False(extensionManager.IsIgnored(provider)); } - private static CodeRefactoringSuggestedAction CreateRefactoringSuggestedAction(EditorTestWorkspace workspace, CodeRefactoringProvider provider, out EditorLayerExtensionManager.ExtensionManager extensionManager) + private static EditorSuggestedActionWithNestedFlavors CreateRefactoringSuggestedAction(EditorTestWorkspace workspace, CodeRefactoringProvider provider, out EditorLayerExtensionManager.ExtensionManager extensionManager) { var codeActions = new List(); RefactoringSetup(workspace, provider, codeActions, out extensionManager, out var textBuffer, out var document); - var suggestedAction = new CodeRefactoringSuggestedAction( + var suggestedAction = new EditorSuggestedActionWithNestedFlavors( workspace.ExportProvider.GetExportedValue(), workspace.ExportProvider.GetExportedValue(), - workspace, document, textBuffer, provider, codeActions.First(), fixAllFlavors: null); + document, textBuffer, provider, codeActions.First(), fixAllFlavors: null, diagnostics: []); return suggestedAction; } diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs index 066e670df8d..ecf1d51565a 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs @@ -179,7 +179,7 @@ public async Task AnalyzerOptionsArePassedToAllAnalyzers() private static void AccessSupportedDiagnostics(DiagnosticAnalyzer analyzer) { var diagnosticService = new HostDiagnosticAnalyzers([new AnalyzerImageReference([analyzer])]); - diagnosticService.GetDiagnosticDescriptorsPerReference(new DiagnosticAnalyzerInfoCache()); + diagnosticService.GetDiagnosticDescriptorsPerReference(new DiagnosticAnalyzerInfoCache(), project: null); } private sealed class ThrowingDoNotCatchDiagnosticAnalyzer : ThrowingDiagnosticAnalyzer, IBuiltInAnalyzer where TLanguageKindEnum : struct @@ -214,7 +214,7 @@ private sealed class CompilationAnalyzerWithSyntaxTreeAnalyzer : DiagnosticAnaly private const string ID = "SyntaxDiagnostic"; private static readonly DiagnosticDescriptor s_syntaxDiagnosticDescriptor = - new DiagnosticDescriptor(ID, title: "Syntax", messageFormat: "Syntax", category: "Test", defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true); + new(ID, title: "Syntax", messageFormat: "Syntax", category: "Test", defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/Diagnostics/MockDiagnosticAnalyzerTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/Diagnostics/MockDiagnosticAnalyzerTests.cs index ce3d0e8006c..a66920b7240 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/Diagnostics/MockDiagnosticAnalyzerTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/Diagnostics/MockDiagnosticAnalyzerTests.cs @@ -20,7 +20,7 @@ public sealed partial class MockDiagnosticAnalyzerTests : AbstractCSharpDiagnost private sealed class MockDiagnosticAnalyzer : DiagnosticAnalyzer { public const string Id = "MockDiagnostic"; - private readonly DiagnosticDescriptor _descriptor = new DiagnosticDescriptor(Id, "MockDiagnostic", "MockDiagnostic", "InternalCategory", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: "https://github.com/dotnet/roslyn"); + private readonly DiagnosticDescriptor _descriptor = new(Id, "MockDiagnostic", "MockDiagnostic", "InternalCategory", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: "https://github.com/dotnet/roslyn"); public override ImmutableArray SupportedDiagnostics { diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/EventHookup/EventHookupCommandHandlerTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/EventHookup/EventHookupCommandHandlerTests.cs index 57fd3ab0d8b..8bb4d30f3c4 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/EventHookup/EventHookupCommandHandlerTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/EventHookup/EventHookupCommandHandlerTests.cs @@ -19,7 +19,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.EventHookup; [Trait(Traits.Feature, Traits.Features.EventHookup)] public sealed class EventHookupCommandHandlerTests { - private readonly NamingStylesTestOptionSets _namingOptions = new NamingStylesTestOptionSets(LanguageNames.CSharp); + private readonly NamingStylesTestOptionSets _namingOptions = new(LanguageNames.CSharp); [WpfFact] public async Task HandlerName_EventInThisClass() @@ -1203,5 +1203,5 @@ public void M2() } private static OptionsCollection QualifyMethodAccessWithNotification(NotificationOption2 notification) - => new OptionsCollection(LanguageNames.CSharp) { { CodeStyleOptions2.QualifyMethodAccess, true, notification } }; + => new(LanguageNames.CSharp) { { CodeStyleOptions2.QualifyMethodAccess, true, notification } }; } diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/EventHookup/EventHookupTestState.cs b/src/roslyn/src/EditorFeatures/CSharpTest/EventHookup/EventHookupTestState.cs index b51f6979de1..a01dbc71102 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/EventHookup/EventHookupTestState.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/EventHookup/EventHookupTestState.cs @@ -48,7 +48,7 @@ public EventHookupTestState(XElement workspaceElement, OptionsCollection options } public static EventHookupTestState CreateTestState(string markup, OptionsCollection options = null) - => new EventHookupTestState(GetWorkspaceXml(markup), options); + => new(GetWorkspaceXml(markup), options); public static XElement GetWorkspaceXml(string markup) => XElement.Parse(string.Format(""" diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.TestFixers.cs b/src/roslyn/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.TestFixers.cs index 2c4af692097..5a21a33eee9 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.TestFixers.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.TestFixers.cs @@ -121,22 +121,16 @@ private sealed class ModifySolutionFixAll : FixAllProvider } [PartNotDiscoverable, Shared, ExportCodeFixProvider(LanguageNames.CSharp)] - private sealed class TestThirdPartyCodeFixDoesNotSupportDocumentScope : TestThirdPartyCodeFix + [method: ImportingConstructor] + [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + private sealed class TestThirdPartyCodeFixDoesNotSupportDocumentScope() : TestThirdPartyCodeFix { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public TestThirdPartyCodeFixDoesNotSupportDocumentScope() - { - } - public override FixAllProvider GetFixAllProvider() => new ModifySolutionFixAll(); private sealed class ModifySolutionFixAll : FixAllProvider { public override IEnumerable GetSupportedFixAllScopes() - { - return new[] { FixAllScope.Project, FixAllScope.Solution, FixAllScope.Custom }; - } + => [FixAllScope.Project, FixAllScope.Solution, FixAllScope.Custom]; public override Task GetFixAsync(FixAllContext fixAllContext) { diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/Interactive/NavigateTo/InteractiveNavigateToTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/Interactive/NavigateTo/InteractiveNavigateToTests.cs index b1e058cbcc6..18b3038afc1 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/Interactive/NavigateTo/InteractiveNavigateToTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/Interactive/NavigateTo/InteractiveNavigateToTests.cs @@ -501,11 +501,11 @@ static C2() { var expecteditems = new List { - new NavigateToItem("C1", NavigateToItemKind.Class, "csharp", "C1", null, s_emptyPrefixPatternMatch, null), - new NavigateToItem("C1", NavigateToItemKind.Method, "csharp", "C1", null, s_emptyPrefixPatternMatch, null), - new NavigateToItem("C2", NavigateToItemKind.Class, "csharp", "C2", null, s_emptyPrefixPatternMatch, null), - new NavigateToItem("C2", NavigateToItemKind.Method, "csharp", "C2", null, s_emptyPrefixPatternMatch, null), // this is the static ctor - new NavigateToItem("C2", NavigateToItemKind.Method, "csharp", "C2", null, s_emptyPrefixPatternMatch, null), + new("C1", NavigateToItemKind.Class, "csharp", "C1", null, s_emptyPrefixPatternMatch, null), + new("C1", NavigateToItemKind.Method, "csharp", "C1", null, s_emptyPrefixPatternMatch, null), + new("C2", NavigateToItemKind.Class, "csharp", "C2", null, s_emptyPrefixPatternMatch, null), + new("C2", NavigateToItemKind.Method, "csharp", "C2", null, s_emptyPrefixPatternMatch, null), // this is the static ctor + new("C2", NavigateToItemKind.Method, "csharp", "C2", null, s_emptyPrefixPatternMatch, null), }; var items = (await _aggregator.GetItemsAsync("C")).ToList(); items.Sort(CompareNavigateToItems); diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs index fe7fd8711e3..f2cdbac7279 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs @@ -161,10 +161,10 @@ public class C """, description, []); } - private static async Task AssertContentIsAsync(EditorTestWorkspace workspace, Document document, int position, string expectedDescription, + private static async Task AssertContentIsAsync(Document document, int position, string expectedDescription, ImmutableArray relatedSpans) { - var info = await GetQuickinfo(workspace, document, position); + var info = await GetQuickInfo(document, position); var description = info?.Sections.FirstOrDefault(s => s.Kind == QuickInfoSectionKinds.Description); Assert.NotNull(description); Assert.Equal(expectedDescription, description.Text); @@ -172,17 +172,16 @@ private static async Task AssertContentIsAsync(EditorTestWorkspace workspace, Do [.. info.RelatedSpans.Select(actualSpan => new Action(expectedSpan => Assert.Equal(expectedSpan, actualSpan)))]); } - private static async Task GetQuickinfo(EditorTestWorkspace workspace, Document document, int position) + private static async Task GetQuickInfo(Document document, int position) { - var sharedGlobalCache = workspace.ExportProvider.GetExportedValue(); - var provider = new CSharpDiagnosticAnalyzerQuickInfoProvider(sharedGlobalCache); + var provider = new CSharpDiagnosticAnalyzerQuickInfoProvider(); var info = await provider.GetQuickInfoAsync(new QuickInfoContext(document, position, SymbolDescriptionOptions.Default, CancellationToken.None)); return info; } - private static async Task AssertNoContentAsync(EditorTestWorkspace workspace, Document document, int position) + private static async Task AssertNoContentAsync(Document document, int position) { - var info = await GetQuickinfo(workspace, document, position); + var info = await GetQuickInfo(document, position); Assert.Null(info); } @@ -201,11 +200,11 @@ private static async Task TestAsync( var document = workspace.CurrentSolution.Projects.First().Documents.First(); if (string.IsNullOrEmpty(expectedDescription)) { - await AssertNoContentAsync(workspace, document, position); + await AssertNoContentAsync(document, position); } else { - await AssertContentIsAsync(workspace, document, position, expectedDescription, relatedSpans); + await AssertContentIsAsync(document, position, expectedDescription, relatedSpans); } } diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/AttributeSignatureHelpProviderTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/AttributeSignatureHelpProviderTests.cs index 38425d1f981..916e5470af3 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/AttributeSignatureHelpProviderTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/AttributeSignatureHelpProviderTests.cs @@ -28,7 +28,7 @@ public async Task TestInvocationWithoutParameters() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) + new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) }; await TestAsync(""" @@ -48,7 +48,7 @@ public async Task TestInvocationWithoutParametersMethodXmlComments() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute()", "Summary For Attribute", null, currentParameterIndex: 0) + new("SomethingAttribute()", "Summary For Attribute", null, currentParameterIndex: 0) }; await TestAsync(""" @@ -70,8 +70,8 @@ public async Task PickCorrectOverload_PickInt() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute(int i)", currentParameterIndex: 0, isSelected: true), - new SignatureHelpTestItem("SomethingAttribute(string i)", currentParameterIndex: 0), + new("SomethingAttribute(int i)", currentParameterIndex: 0, isSelected: true), + new("SomethingAttribute(string i)", currentParameterIndex: 0), }; await TestAsync(""" @@ -91,8 +91,8 @@ public async Task PickCorrectOverload_PickString() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute(int i)", currentParameterIndex: 0), - new SignatureHelpTestItem("SomethingAttribute(string i)", currentParameterIndex: 0, isSelected: true), + new("SomethingAttribute(int i)", currentParameterIndex: 0), + new("SomethingAttribute(string i)", currentParameterIndex: 0, isSelected: true), }; await TestAsync(""" @@ -112,7 +112,7 @@ public async Task TestInvocationWithParametersOn1() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute(int someInteger, string someString)", string.Empty, string.Empty, currentParameterIndex: 0) + new("SomethingAttribute(int someInteger, string someString)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -133,7 +133,7 @@ public async Task TestInvocationWithParametersXmlCommentsOn1() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute(int someInteger, string someString)", "Summary For Attribute", "Param someInteger", currentParameterIndex: 0) + new("SomethingAttribute(int someInteger, string someString)", "Summary For Attribute", "Param someInteger", currentParameterIndex: 0) }; await TestAsync(""" @@ -159,7 +159,7 @@ public async Task TestInvocationWithParametersOn2() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute(int someInteger, string someString)", string.Empty, string.Empty, currentParameterIndex: 1) + new("SomethingAttribute(int someInteger, string someString)", string.Empty, string.Empty, currentParameterIndex: 1) }; await TestAsync(""" @@ -180,7 +180,7 @@ public async Task TestInvocationWithParametersXmlComentsOn2() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute(int someInteger, string someString)", "Summary For Attribute", "Param someString", currentParameterIndex: 1) + new("SomethingAttribute(int someInteger, string someString)", "Summary For Attribute", "Param someString", currentParameterIndex: 1) }; await TestAsync(""" @@ -206,7 +206,7 @@ public async Task TestInvocationWithClosingParen() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) + new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) }; await TestAsync(""" @@ -297,7 +297,7 @@ public async Task TestAttributeWithValidField() { var expectedOrderedItems = new List { - new SignatureHelpTestItem($"SomethingAttribute({FeaturesResources.Properties}: [goo = int])", string.Empty, string.Empty, currentParameterIndex: 0) + new($"SomethingAttribute({FeaturesResources.Properties}: [goo = int])", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -318,7 +318,7 @@ public async Task TestAttributeWithInvalidFieldReadonly() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) + new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) }; await TestAsync(""" @@ -339,7 +339,7 @@ public async Task TestAttributeWithInvalidFieldStatic() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) + new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) }; await TestAsync(""" @@ -360,7 +360,7 @@ public async Task TestAttributeWithInvalidFieldConst() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) + new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) }; await TestAsync(""" @@ -386,7 +386,7 @@ public async Task TestAttributeWithValidProperty() { var expectedOrderedItems = new List { - new SignatureHelpTestItem($"SomethingAttribute({FeaturesResources.Properties}: [goo = int])", string.Empty, string.Empty, currentParameterIndex: 0) + new($"SomethingAttribute({FeaturesResources.Properties}: [goo = int])", string.Empty, string.Empty, currentParameterIndex: 0) }; // TODO: Bug 12319: Enable tests for script when this is fixed. @@ -408,7 +408,7 @@ public async Task TestAttributeWithInvalidPropertyStatic() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) + new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) }; await TestAsync(""" @@ -429,7 +429,7 @@ public async Task TestAttributeWithInvalidPropertyNoSetter() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) + new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) }; await TestAsync(""" @@ -450,7 +450,7 @@ public async Task TestAttributeWithInvalidPropertyNoGetter() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) + new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) }; await TestAsync(""" @@ -471,7 +471,7 @@ public async Task TestAttributeWithInvalidPropertyPrivateGetter() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) + new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) }; await TestAsync(""" @@ -492,7 +492,7 @@ public async Task TestAttributeWithInvalidPropertyPrivateSetter() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) + new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) }; await TestAsync(""" @@ -514,7 +514,7 @@ public async Task TestAttributeWithOverriddenProperty() { var expectedOrderedItems = new List { - new SignatureHelpTestItem($"DerivedAttribute({FeaturesResources.Properties}: [Name = string])", string.Empty, string.Empty, currentParameterIndex: 0) + new($"DerivedAttribute({FeaturesResources.Properties}: [Name = string])", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -548,7 +548,7 @@ public async Task TestAttributeWithArgumentsAndNamedParameters1() { var expectedOrderedItems = new List { - new SignatureHelpTestItem($"SomethingAttribute([int goo = 0], [string bar = null], {FeaturesResources.Properties}: [fieldbar = string], [fieldfoo = int])", string.Empty, "GooParameter", currentParameterIndex: 0) + new($"SomethingAttribute([int goo = 0], [string bar = null], {FeaturesResources.Properties}: [fieldbar = string], [fieldfoo = int])", string.Empty, "GooParameter", currentParameterIndex: 0) }; // TODO: Bug 12319: Enable tests for script when this is fixed. @@ -575,7 +575,7 @@ public async Task TestAttributeWithArgumentsAndNamedParameters2() { var expectedOrderedItems = new List { - new SignatureHelpTestItem($"SomethingAttribute([int goo = 0], [string bar = null], {FeaturesResources.Properties}: [fieldbar = string], [fieldfoo = int])", string.Empty, "BarParameter", currentParameterIndex: 1) + new($"SomethingAttribute([int goo = 0], [string bar = null], {FeaturesResources.Properties}: [fieldbar = string], [fieldfoo = int])", string.Empty, "BarParameter", currentParameterIndex: 1) }; // TODO: Bug 12319: Enable tests for script when this is fixed. @@ -602,7 +602,7 @@ public async Task TestAttributeWithArgumentsAndNamedParameters3() { var expectedOrderedItems = new List { - new SignatureHelpTestItem($"SomethingAttribute([int goo = 0], [string bar = null], {FeaturesResources.Properties}: [fieldbar = string], [fieldfoo = int])", string.Empty, string.Empty, currentParameterIndex: 2) + new($"SomethingAttribute([int goo = 0], [string bar = null], {FeaturesResources.Properties}: [fieldbar = string], [fieldfoo = int])", string.Empty, string.Empty, currentParameterIndex: 2) }; // TODO: Bug 12319: Enable tests for script when this is fixed. @@ -629,7 +629,7 @@ public async Task TestAttributeWithOptionalArgumentAndNamedParameterWithSameName { var expectedOrderedItems = new List { - new SignatureHelpTestItem($"SomethingAttribute([int goo = 0], {FeaturesResources.Properties}: [goo = int])", string.Empty, "GooParameter", currentParameterIndex: 0) + new($"SomethingAttribute([int goo = 0], {FeaturesResources.Properties}: [goo = int])", string.Empty, "GooParameter", currentParameterIndex: 0) }; // TODO: Bug 12319: Enable tests for script when this is fixed. @@ -654,7 +654,7 @@ public async Task TestAttributeWithOptionalArgumentAndNamedParameterWithSameName { var expectedOrderedItems = new List { - new SignatureHelpTestItem($"SomethingAttribute([int goo = 0], {FeaturesResources.Properties}: [goo = int])", string.Empty, string.Empty, currentParameterIndex: 1) + new($"SomethingAttribute([int goo = 0], {FeaturesResources.Properties}: [goo = int])", string.Empty, string.Empty, currentParameterIndex: 1) }; // TODO: Bug 12319: Enable tests for script when this is fixed. @@ -682,7 +682,7 @@ public async Task TestInvocationOnTriggerParens() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute(int someParameter, bool somethingElse)", string.Empty, string.Empty, currentParameterIndex: 0) + new("SomethingAttribute(int someParameter, bool somethingElse)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -705,7 +705,7 @@ public async Task TestInvocationOnTriggerComma() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute(int someParameter, bool somethingElse)", string.Empty, string.Empty, currentParameterIndex: 1) + new("SomethingAttribute(int someParameter, bool somethingElse)", string.Empty, string.Empty, currentParameterIndex: 1) }; await TestAsync(""" @@ -776,7 +776,7 @@ public MyAttribute(int x) var expectedOrderedItems = new List { - new SignatureHelpTestItem("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -809,7 +809,7 @@ public MyAttribute(int x) var expectedOrderedItems = new List { - new SignatureHelpTestItem("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -842,7 +842,7 @@ public MyAttribute(int x) var expectedOrderedItems = new List { - new SignatureHelpTestItem("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -889,13 +889,13 @@ public MyAttribute(int x, int y) var expectedOrderedItemsMetadataReference = new List { - new SignatureHelpTestItem("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0) }; var expectedOrderedItemsSameSolution = new List { - new SignatureHelpTestItem("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0), - new SignatureHelpTestItem("MyAttribute(int x, int y)", string.Empty, string.Empty, currentParameterIndex: 0) + new("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0), + new("MyAttribute(int x, int y)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/ConstructorInitializerSignatureHelpProviderTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/ConstructorInitializerSignatureHelpProviderTests.cs index bff5937f6bf..ccb177b567b 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/ConstructorInitializerSignatureHelpProviderTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/ConstructorInitializerSignatureHelpProviderTests.cs @@ -28,7 +28,7 @@ public async Task TestInvocationWithoutParameters() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("BaseClass()", string.Empty, null, currentParameterIndex: 0) + new("BaseClass()", string.Empty, null, currentParameterIndex: 0) }; await TestAsync(""" @@ -50,7 +50,7 @@ public async Task TestInvocationWithoutParametersMethodXmlComments() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("BaseClass()", "Summary for BaseClass", null, currentParameterIndex: 0) + new("BaseClass()", "Summary for BaseClass", null, currentParameterIndex: 0) }; await TestAsync(""" @@ -73,7 +73,7 @@ public async Task TestInvocationWithParametersOn1() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("BaseClass(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 0) + new("BaseClass(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -95,7 +95,7 @@ public async Task TestInvocationWithParametersXmlCommentsOn1() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("BaseClass(int a, int b)", "Summary for BaseClass", "Param a", currentParameterIndex: 0) + new("BaseClass(int a, int b)", "Summary for BaseClass", "Param a", currentParameterIndex: 0) }; await TestAsync(""" @@ -120,7 +120,7 @@ public async Task TestInvocationWithParametersOn2() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("BaseClass(int a, int b)", "Summary for BaseClass", "Param b", currentParameterIndex: 1) + new("BaseClass(int a, int b)", "Summary for BaseClass", "Param b", currentParameterIndex: 1) }; await TestAsync(""" @@ -146,7 +146,7 @@ public async Task TestInvocationWithParametersXmlComentsOn2() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("BaseClass(int a, int b)", "Summary for BaseClass", "Param b", currentParameterIndex: 1) + new("BaseClass(int a, int b)", "Summary for BaseClass", "Param b", currentParameterIndex: 1) }; await TestAsync(""" @@ -171,7 +171,7 @@ public async Task TestThisInvocation() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("Goo(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 1) + new("Goo(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 1) }; await TestAsync(""" @@ -188,7 +188,7 @@ public async Task TestThisInvocationWithNonEmptyArgumentList() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("Foo()", string.Empty, null, currentParameterIndex: 0), + new("Foo()", string.Empty, null, currentParameterIndex: 0), }; await TestAsync(""" @@ -205,7 +205,7 @@ public async Task TestInvocationWithoutClosingParen() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("Goo(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 1) + new("Goo(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 1) }; await TestAsync(""" @@ -222,7 +222,7 @@ public async Task TestThisInvocationWithoutClosingParenWithNonEmptyArgumentList( { var expectedOrderedItems = new List { - new SignatureHelpTestItem("Foo()", string.Empty, null, currentParameterIndex: 0), + new("Foo()", string.Empty, null, currentParameterIndex: 0), }; await TestAsync(""" @@ -239,8 +239,8 @@ public async Task PickCorrectOverload_PickInt() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("D(int i)", currentParameterIndex: 0, isSelected: true), - new SignatureHelpTestItem("D(string i)", currentParameterIndex: 0), + new("D(int i)", currentParameterIndex: 0, isSelected: true), + new("D(string i)", currentParameterIndex: 0), }; await TestAsync(""" @@ -260,8 +260,8 @@ public async Task PickCorrectOverload_PickString() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("D(int i)", currentParameterIndex: 0), - new SignatureHelpTestItem("D(string i)", currentParameterIndex: 0, isSelected: true), + new("D(int i)", currentParameterIndex: 0), + new("D(string i)", currentParameterIndex: 0, isSelected: true), }; await TestAsync(""" @@ -299,7 +299,7 @@ public async Task TestInvocationOnTriggerParens() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("Goo(int a)", string.Empty, string.Empty, currentParameterIndex: 0) + new("Goo(int a)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -316,7 +316,7 @@ public async Task TestInvocationOnTriggerParensWithNonEmptyArgumentList() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("Foo()", string.Empty, null, currentParameterIndex: 0), + new("Foo()", string.Empty, null, currentParameterIndex: 0), }; await TestAsync(""" @@ -333,7 +333,7 @@ public async Task TestInvocationOnTriggerComma() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("Goo(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 1) + new("Goo(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 1) }; await TestAsync(""" @@ -350,7 +350,7 @@ public async Task TestInvocationOnTriggerCommaWithNonEmptyArgumentList() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("Foo()", string.Empty, null, currentParameterIndex: 0), + new("Foo()", string.Empty, null, currentParameterIndex: 0), }; await TestAsync(""" @@ -407,7 +407,7 @@ public BaseClass(int x) """; var expectedOrderedItems = new List { - new SignatureHelpTestItem("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -438,7 +438,7 @@ public BaseClass(int x) """; var expectedOrderedItems = new List { - new SignatureHelpTestItem("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -469,7 +469,7 @@ public BaseClass(int x) """; var expectedOrderedItems = new List { - new SignatureHelpTestItem("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -513,13 +513,13 @@ public BaseClass(int x, int y) """; var expectedOrderedItemsMetadataReference = new List { - new SignatureHelpTestItem("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0) }; var expectedOrderedItemsSameSolution = new List { - new SignatureHelpTestItem("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0), - new SignatureHelpTestItem("BaseClass(int x, int y)", string.Empty, string.Empty, currentParameterIndex: 0) + new("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0), + new("BaseClass(int x, int y)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -647,7 +647,7 @@ public async Task TypingTupleDoesNotDismiss1() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("D(object o)", currentParameterIndex: 0) + new("D(object o)", currentParameterIndex: 0) }; await TestAsync(""" @@ -665,7 +665,7 @@ public async Task TypingTupleDoesNotDismiss2() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("D(object o)", currentParameterIndex: 0) + new("D(object o)", currentParameterIndex: 0) }; await TestAsync(""" @@ -682,7 +682,7 @@ public async Task TypingTupleDoesNotDismiss3() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("D(object o)", currentParameterIndex: 0) + new("D(object o)", currentParameterIndex: 0) }; await TestAsync(""" @@ -700,7 +700,7 @@ public async Task TypingTupleDoesNotDismiss4() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("D(object o)", currentParameterIndex: 0) + new("D(object o)", currentParameterIndex: 0) }; await TestAsync(""" @@ -725,7 +725,7 @@ public async Task PickCorrectOverload_NamesAndEmptyPositions(string arguments, i { var expectedOrderedItems = new List { - new SignatureHelpTestItem("Program(int i1, int i2, int i3)", currentParameterIndex: expectedParameterIndex, isSelected: true), + new("Program(int i1, int i2, int i3)", currentParameterIndex: expectedParameterIndex, isSelected: true), }; await TestAsync($$""" @@ -759,8 +759,8 @@ public async Task PickCorrectOverload_Incomplete(string arguments, int expectedP var index = 0; var expectedOrderedItems = new List { - new SignatureHelpTestItem("Program(int i, string s)", currentParameterIndex: expectedParameterIndex, isSelected: expecteSelectedIndex == index++), - new SignatureHelpTestItem("Program(string s, string s2)", currentParameterIndex: expectedParameterIndex, isSelected: expecteSelectedIndex == index++), + new("Program(int i, string s)", currentParameterIndex: expectedParameterIndex, isSelected: expecteSelectedIndex == index++), + new("Program(string s, string s2)", currentParameterIndex: expectedParameterIndex, isSelected: expecteSelectedIndex == index++), }; await TestAsync($$""" diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/ElementAccessExpressionSignatureHelpProviderTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/ElementAccessExpressionSignatureHelpProviderTests.cs index 7ab810931f8..460ee6ad0da 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/ElementAccessExpressionSignatureHelpProviderTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/ElementAccessExpressionSignatureHelpProviderTests.cs @@ -28,7 +28,7 @@ public async Task TestInvocationWithParametersOn1() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) + new("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -57,7 +57,7 @@ public async Task TestInvocationWithParametersOn1_WithRefReturn() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("ref int C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) + new("ref int C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -80,7 +80,7 @@ public async Task TestInvocationWithParametersOn1_WithRefReadonlyReturn() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("ref readonly int C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) + new("ref readonly int C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -103,7 +103,7 @@ public async Task TestInvocationOnExpression() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) + new("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -132,7 +132,7 @@ public async Task TestInvocationWithParametersXmlCommentsOn1() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a]", "Summary for this.", "Param a", currentParameterIndex: 0) + new("string C[int a]", "Summary for this.", "Param a", currentParameterIndex: 0) }; await TestAsync(""" @@ -165,7 +165,7 @@ public async Task TestInvocationWithParametersOn2() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a, bool b]", string.Empty, string.Empty, currentParameterIndex: 1) + new("string C[int a, bool b]", string.Empty, string.Empty, currentParameterIndex: 1) }; await TestAsync(""" @@ -194,7 +194,7 @@ public async Task TestInvocationWithParametersXmlComentsOn2() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a, bool b]", "Summary for this.", "Param b", currentParameterIndex: 1) + new("string C[int a, bool b]", "Summary for this.", "Param b", currentParameterIndex: 1) }; await TestAsync(""" @@ -228,7 +228,7 @@ public async Task TestInvocationWithoutClosingBracketWithParameters() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) + new("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -257,7 +257,7 @@ public async Task TestInvocationWithoutClosingBracketWithParametersOn2() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a, bool b]", string.Empty, string.Empty, currentParameterIndex: 1) + new("string C[int a, bool b]", string.Empty, string.Empty, currentParameterIndex: 1) }; await TestAsync(""" @@ -316,7 +316,7 @@ public async Task TestInvocationOnTriggerBracket() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) + new("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -345,7 +345,7 @@ public async Task TestInvocationOnTriggerComma() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a, bool b]", string.Empty, string.Empty, currentParameterIndex: 1) + new("string C[int a, bool b]", string.Empty, string.Empty, currentParameterIndex: 1) }; await TestAsync(""" @@ -433,7 +433,7 @@ public int this[int x] """; var expectedOrderedItems = new List { - new SignatureHelpTestItem("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) + new("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -470,7 +470,7 @@ public int this[int x] """; var expectedOrderedItemsMetadataReference = new List { - new SignatureHelpTestItem("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) + new("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -507,7 +507,7 @@ public int this[int x] """; var expectedOrderedItems = new List { - new SignatureHelpTestItem("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) + new("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -560,13 +560,13 @@ public int this[double d] var expectedOrderedItemsMetadataReference = new List { - new SignatureHelpTestItem("int Goo[double d]", string.Empty, string.Empty, currentParameterIndex: 0) + new("int Goo[double d]", string.Empty, string.Empty, currentParameterIndex: 0) }; var expectedOrderedItemsSameSolution = new List { - new SignatureHelpTestItem("int Goo[double d]", string.Empty, string.Empty, currentParameterIndex: 0), - new SignatureHelpTestItem("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0), + new("int Goo[double d]", string.Empty, string.Empty, currentParameterIndex: 0), + new("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0), }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -603,7 +603,7 @@ public int this[int x] """; var expectedOrderedItems = new List { - new SignatureHelpTestItem("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) + new("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -640,7 +640,7 @@ public int this[int x] """; var expectedOrderedItems = new List { - new SignatureHelpTestItem("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) + new("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -678,7 +678,7 @@ public int this[int x] """; var expectedOrderedItems = new List { - new SignatureHelpTestItem("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) + new("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -739,12 +739,12 @@ End Class var metadataItems = new List { - new SignatureHelpTestItem("string CCC.IndexProp[int p1]", string.Empty, string.Empty, currentParameterIndex: 0) + new("string CCC.IndexProp[int p1]", string.Empty, string.Empty, currentParameterIndex: 0) }; var projectReferenceItems = new List { - new SignatureHelpTestItem("string CCC.IndexProp[int p1]", "An index property from VB", "p1 is an integer index", currentParameterIndex: 0) + new("string CCC.IndexProp[int p1]", "An index property from VB", "p1 is an integer index", currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -856,7 +856,7 @@ public async Task TestInvocation() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) + new("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -885,7 +885,7 @@ public async Task ConditionalIndexer() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("int P[int z]", string.Empty, string.Empty, currentParameterIndex: 0) + new("int P[int z]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNamePartiallyWrittenSignatureHelpProviderTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNamePartiallyWrittenSignatureHelpProviderTests.cs index 03868077884..15833b8b3f3 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNamePartiallyWrittenSignatureHelpProviderTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNamePartiallyWrittenSignatureHelpProviderTests.cs @@ -27,7 +27,7 @@ public async Task NestedGenericUnterminated() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 0) + new("G", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -48,7 +48,7 @@ public async Task NestedGenericUnterminatedWithAmbiguousShift() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 0) + new("G", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -71,7 +71,7 @@ public async Task NestedGenericUnterminatedWithAmbiguousUnsignedShift() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 0) + new("G", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -94,7 +94,7 @@ public async Task DeclaringGenericTypeWith1ParameterUnterminated() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 0) + new("G", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -115,7 +115,7 @@ public async Task CallingGenericAsyncMethod() { var expectedOrderedItems = new List { - new SignatureHelpTestItem($"({CSharpFeaturesResources.awaitable}) Task Program.Goo()", methodDocumentation: string.Empty, string.Empty, currentParameterIndex: 0) + new($"({CSharpFeaturesResources.awaitable}) Task Program.Goo()", methodDocumentation: string.Empty, string.Empty, currentParameterIndex: 0) }; // TODO: Enable the script case when we have support for extension methods in scripts @@ -159,7 +159,7 @@ public void Goo(T x) var expectedOrderedItems = new List { - new SignatureHelpTestItem("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -194,7 +194,7 @@ public void Goo(T x) var expectedOrderedItems = new List { - new SignatureHelpTestItem("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -229,7 +229,7 @@ public void Goo(T x) var expectedOrderedItems = new List { - new SignatureHelpTestItem("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -276,13 +276,13 @@ public void Goo(T x, U y) """; var expectedOrderedItemsMetadataReference = new List { - new SignatureHelpTestItem("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0) }; var expectedOrderedItemsSameSolution = new List { - new SignatureHelpTestItem("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0), - new SignatureHelpTestItem("void C.Goo(T x, U y)", string.Empty, string.Empty, currentParameterIndex: 0) + new("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0), + new("void C.Goo(T x, U y)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -298,8 +298,8 @@ public async Task GenericExtensionMethod() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("void IGoo.Bar()", currentParameterIndex: 0), - new SignatureHelpTestItem($"({CSharpFeaturesResources.extension}) void IGoo.Bar()", currentParameterIndex: 0), + new("void IGoo.Bar()", currentParameterIndex: 0), + new($"({CSharpFeaturesResources.extension}) void IGoo.Bar()", currentParameterIndex: 0), }; // Extension methods are supported in Interactive/Script (yet). @@ -330,7 +330,7 @@ public async Task InvokingGenericMethodWith1ParameterUnterminated() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("void C.Goo()", + new("void C.Goo()", "Method Goo", "Method type parameter", currentParameterIndex: 0) }; @@ -356,7 +356,7 @@ public async Task TestInvocationOnTriggerBracket() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 0) + new("G", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -377,7 +377,7 @@ public async Task TestInvocationOnTriggerComma() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 1) + new("G", string.Empty, string.Empty, currentParameterIndex: 1) }; await TestAsync(""" diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNameSignatureHelpProviderTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNameSignatureHelpProviderTests.cs index 62adec90b36..dedfddaf80d 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNameSignatureHelpProviderTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNameSignatureHelpProviderTests.cs @@ -31,7 +31,7 @@ public async Task NestedGenericTerminated() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 0) + new("G", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -52,7 +52,7 @@ public async Task DeclaringGenericTypeWith1ParameterTerminated() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 0) + new("G", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -73,7 +73,7 @@ public async Task DeclaringGenericTypeWith2ParametersOn1() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 0) + new("G", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -94,7 +94,7 @@ public async Task DeclaringGenericTypeWith2ParametersOn2() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 1) + new("G", string.Empty, string.Empty, currentParameterIndex: 1) }; await TestAsync(""" @@ -115,7 +115,7 @@ public async Task DeclaringGenericTypeWith2ParametersOn1XmlDoc() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G", + new("G", "Summary for G", "TypeParamS. Also see T", currentParameterIndex: 0) @@ -144,7 +144,7 @@ public async Task DeclaringGenericTypeWith2ParametersOn2XmlDoc() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G", "Summary for G", "TypeParamT. Also see S", currentParameterIndex: 1) + new("G", "Summary for G", "TypeParamT. Also see S", currentParameterIndex: 1) }; await TestAsync(""" @@ -174,7 +174,7 @@ public async Task DeclaringGenericTypeWithConstraintsStruct() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G where S : struct", string.Empty, string.Empty, currentParameterIndex: 0) + new("G where S : struct", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -196,7 +196,7 @@ public async Task DeclaringGenericTypeWithConstraintsClass() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G where S : class", string.Empty, string.Empty, currentParameterIndex: 0) + new("G where S : class", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -218,7 +218,7 @@ public async Task DeclaringGenericTypeWithConstraintsNew() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G where S : new()", string.Empty, string.Empty, currentParameterIndex: 0) + new("G where S : new()", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -240,7 +240,7 @@ public async Task DeclaringGenericTypeWithConstraintsBase() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G where S : Base", string.Empty, string.Empty, currentParameterIndex: 0) + new("G where S : Base", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -264,7 +264,7 @@ public async Task DeclaringGenericTypeWithConstraintsBaseGenericWithGeneric() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G where S : Base", string.Empty, string.Empty, currentParameterIndex: 0) + new("G where S : Base", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -288,7 +288,7 @@ public async Task DeclaringGenericTypeWithConstraintsBaseGenericWithNonGeneric() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G where S : Base", string.Empty, string.Empty, currentParameterIndex: 0) + new("G where S : Base", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -312,7 +312,7 @@ public async Task DeclaringGenericTypeWithConstraintsBaseGenericNested() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G where S : Base>", string.Empty, string.Empty, currentParameterIndex: 0) + new("G where S : Base>", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -336,7 +336,7 @@ public async Task DeclaringGenericTypeWithConstraintsDeriveFromAnotherGenericPar { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G where S : T", string.Empty, string.Empty, currentParameterIndex: 0) + new("G where S : T", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -358,7 +358,7 @@ public async Task DeclaringGenericTypeWithConstraintsMixed1() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G where S : Base, new()", "Summary1", "SummaryS", currentParameterIndex: 0) + new("G where S : Base, new()", "Summary1", "SummaryS", currentParameterIndex: 0) }; await TestAsync(""" @@ -391,7 +391,7 @@ public async Task DeclaringGenericTypeWithConstraintsMixed2() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G where T : class, S, IGoo, new()", "Summary1", "SummaryT", currentParameterIndex: 1) + new("G where T : class, S, IGoo, new()", "Summary1", "SummaryT", currentParameterIndex: 1) }; await TestAsync(""" @@ -424,7 +424,7 @@ public async Task DeclaringGenericTypeWithConstraintsAllowRefStruct() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G where S : allows ref struct", string.Empty, string.Empty, currentParameterIndex: 0) + new("G where S : allows ref struct", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -450,7 +450,7 @@ public async Task InvokingGenericMethodWith1ParameterTerminated() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("void C.Goo()", string.Empty, string.Empty, currentParameterIndex: 0) + new("void C.Goo()", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -471,7 +471,7 @@ public async Task InvokingGenericMethodWith2ParametersOn1() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("void C.Goo(S s, T t)", + new("void C.Goo(S s, T t)", "Method summary", "type param S. see T", currentParameterIndex: 0) }; @@ -500,7 +500,7 @@ public async Task InvokingGenericMethodWith2ParametersOn2() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("void C.Goo(S s, T t)", string.Empty, string.Empty, currentParameterIndex: 1) + new("void C.Goo(S s, T t)", string.Empty, string.Empty, currentParameterIndex: 1) }; await TestAsync(""" @@ -521,7 +521,7 @@ public async Task InvokingGenericMethodWith2ParametersOn1XmlDoc() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("void C.Goo(S s, T t)", "SummaryForGoo", "SummaryForS", currentParameterIndex: 0) + new("void C.Goo(S s, T t)", "SummaryForGoo", "SummaryForS", currentParameterIndex: 0) }; await TestAsync(""" @@ -547,7 +547,7 @@ public async Task InvokingGenericMethodWith2ParametersOn2XmlDoc() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("void C.Goo(S s, T t)", "SummaryForGoo", "SummaryForT", currentParameterIndex: 1) + new("void C.Goo(S s, T t)", "SummaryForGoo", "SummaryForT", currentParameterIndex: 1) }; await TestAsync(""" @@ -573,7 +573,7 @@ public async Task CallingGenericExtensionMethod() { var expectedOrderedItems = new List { - new SignatureHelpTestItem($"({CSharpFeaturesResources.extension}) void G.Goo()", string.Empty, string.Empty, currentParameterIndex: 0) + new($"({CSharpFeaturesResources.extension}) void G.Goo()", string.Empty, string.Empty, currentParameterIndex: 0) }; // TODO: Enable the script case when we have support for extension methods in scripts @@ -606,7 +606,7 @@ public async Task InvokingGenericMethodWithConstraintsMixed1() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("S C.Goo(S s, T t) where S : Base, new()", "GooSummary", "ParamS", currentParameterIndex: 0) + new("S C.Goo(S s, T t) where S : Base, new()", "GooSummary", "ParamS", currentParameterIndex: 0) }; await TestAsync(""" @@ -638,7 +638,7 @@ public async Task InvokingGenericMethodWithConstraintsMixed2() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("S C.Goo(S s, T t) where T : class, S, IGoo, new()", "GooSummary", "ParamT", currentParameterIndex: 1) + new("S C.Goo(S s, T t) where T : class, S, IGoo, new()", "GooSummary", "ParamT", currentParameterIndex: 1) }; await TestAsync(""" @@ -807,7 +807,7 @@ public class C var expectedOrderedItems = new List { - new SignatureHelpTestItem("C", string.Empty, string.Empty, currentParameterIndex: 0) + new("C", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -840,7 +840,7 @@ public class C var expectedOrderedItems = new List { - new SignatureHelpTestItem("C", string.Empty, string.Empty, currentParameterIndex: 0) + new("C", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -873,7 +873,7 @@ public class C var expectedOrderedItems = new List { - new SignatureHelpTestItem("C", string.Empty, string.Empty, currentParameterIndex: 0) + new("C", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -916,7 +916,7 @@ public async Task DeclaringGenericTypeWithDocCommentList() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G", """ + new("G", """ List: Item 1. diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/InvocationExpressionSignatureHelpProviderTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/InvocationExpressionSignatureHelpProviderTests.cs index 7b4d9958c9a..4b6b6f02633 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/InvocationExpressionSignatureHelpProviderTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/InvocationExpressionSignatureHelpProviderTests.cs @@ -1101,8 +1101,8 @@ public void Goo(int x) """; var expectedOrderedItems = new List { - new SignatureHelpTestItem("void B.Goo()", string.Empty, null, currentParameterIndex: 0), - new SignatureHelpTestItem("void D.Goo(int x)", string.Empty, string.Empty, currentParameterIndex: 0), + new("void B.Goo()", string.Empty, null, currentParameterIndex: 0), + new("void D.Goo(int x)", string.Empty, string.Empty, currentParameterIndex: 0), }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, diff --git a/src/roslyn/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs b/src/roslyn/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs index 0cf2a98e170..6429654a57c 100644 --- a/src/roslyn/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs +++ b/src/roslyn/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs @@ -312,7 +312,14 @@ public ValueTask GetUpdatesAsync(CancellationToken canc [Obsolete] public ValueTask GetUpdatesAsync(ImmutableArray runningProjects, CancellationToken cancellationToken) - => throw new NotImplementedException(); + { + // StreamJsonRpc may use this overload when the method is invoked with empty parameters. Call the new implementation instead. + + if (!runningProjects.IsEmpty) + throw new NotImplementedException(); + + return GetUpdatesAsync(ImmutableArray.Empty, cancellationToken); + } public async ValueTask GetUpdatesAsync(ImmutableArray runningProjects, CancellationToken cancellationToken) { diff --git a/src/roslyn/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageServiceBridge.cs b/src/roslyn/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageServiceBridge.cs index a58f500f26d..80b8038ef7e 100644 --- a/src/roslyn/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageServiceBridge.cs +++ b/src/roslyn/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageServiceBridge.cs @@ -38,7 +38,14 @@ public ValueTask GetUpdatesAsync(CancellationToken canc [Obsolete] public ValueTask GetUpdatesAsync(ImmutableArray runningProjects, CancellationToken cancellationToken) - => throw new NotImplementedException(); + { + // StreamJsonRpc may use this overload when the method is invoked with empty parameters. Call the new implementation instead. + + if (!runningProjects.IsEmpty) + throw new NotImplementedException(); + + return GetUpdatesAsync(ImmutableArray.Empty, cancellationToken); + } public ValueTask GetUpdatesAsync(ImmutableArray runningProjects, CancellationToken cancellationToken) => service.GetUpdatesAsync(runningProjects, cancellationToken); diff --git a/src/roslyn/src/EditorFeatures/Core/Editor/EditorLayerExtensionManager.cs b/src/roslyn/src/EditorFeatures/Core/Editor/EditorLayerExtensionManager.cs index 8ad860c76ae..9a4df990d5d 100644 --- a/src/roslyn/src/EditorFeatures/Core/Editor/EditorLayerExtensionManager.cs +++ b/src/roslyn/src/EditorFeatures/Core/Editor/EditorLayerExtensionManager.cs @@ -51,8 +51,8 @@ protected override void HandleNonCancellationException(object provider, Exceptio if (provider is CodeFixProvider or CodeRefactoringProvider - or CodeRefactorings.FixAllProvider - or CodeFixes.FixAllProvider + or RefactorAllProvider + or FixAllProvider or CompletionProvider) { if (!IsIgnored(provider)) diff --git a/src/roslyn/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/IVSTypeScriptFindUsagesContext.cs b/src/roslyn/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/IVSTypeScriptFindUsagesContext.cs index 86b847e0f24..411f1f3843b 100644 --- a/src/roslyn/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/IVSTypeScriptFindUsagesContext.cs +++ b/src/roslyn/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/IVSTypeScriptFindUsagesContext.cs @@ -124,7 +124,7 @@ internal sealed class VSTypeScriptSourceReferenceItem( VSTypeScriptDocumentSpan sourceSpan, VSTypeScriptSymbolUsageInfo symbolUsageInfo) { - internal readonly SourceReferenceItem UnderlyingObject = new SourceReferenceItem( + internal readonly SourceReferenceItem UnderlyingObject = new( definition.UnderlyingObject, sourceSpan.ToDocumentSpan(), classifiedSpans: null, symbolUsageInfo.UnderlyingObject); public VSTypeScriptDocumentSpan GetSourceSpan() diff --git a/src/roslyn/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptDebugLocationInfoWrapper.cs b/src/roslyn/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptDebugLocationInfoWrapper.cs index 6d7a280f8f1..9d7ce76763c 100644 --- a/src/roslyn/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptDebugLocationInfoWrapper.cs +++ b/src/roslyn/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptDebugLocationInfoWrapper.cs @@ -8,7 +8,7 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; internal readonly struct VSTypeScriptDebugLocationInfoWrapper(string name, int lineOffset) { - internal readonly DebugLocationInfo UnderlyingObject = new DebugLocationInfo(name, lineOffset); + internal readonly DebugLocationInfo UnderlyingObject = new(name, lineOffset); public readonly string Name => UnderlyingObject.Name; public readonly int LineOffset => UnderlyingObject.LineOffset; diff --git a/src/roslyn/src/EditorFeatures/Core/Host/IPreviewPaneService.cs b/src/roslyn/src/EditorFeatures/Core/Host/IPreviewPaneService.cs index 817006e99a8..e963568c775 100644 --- a/src/roslyn/src/EditorFeatures/Core/Host/IPreviewPaneService.cs +++ b/src/roslyn/src/EditorFeatures/Core/Host/IPreviewPaneService.cs @@ -10,5 +10,5 @@ namespace Microsoft.CodeAnalysis.Editor.Host; internal interface IPreviewPaneService : IWorkspaceService { - object GetPreviewPane(DiagnosticData diagnostic, IReadOnlyList previewContent); + object GetPreviewPane(DiagnosticData? diagnostic, IReadOnlyList previewContent); } diff --git a/src/roslyn/src/EditorFeatures/Core/InlineRename/AbstractInlineRenameUndoManager.cs b/src/roslyn/src/EditorFeatures/Core/InlineRename/AbstractInlineRenameUndoManager.cs index 45e07d043b6..9a7eac5caa9 100644 --- a/src/roslyn/src/EditorFeatures/Core/InlineRename/AbstractInlineRenameUndoManager.cs +++ b/src/roslyn/src/EditorFeatures/Core/InlineRename/AbstractInlineRenameUndoManager.cs @@ -28,8 +28,8 @@ protected sealed class ActiveSpanState protected readonly InlineRenameService InlineRenameService; protected readonly Dictionary UndoManagers = []; - protected readonly Stack UndoStack = new Stack(); - protected readonly Stack RedoStack = new Stack(); + protected readonly Stack UndoStack = new(); + protected readonly Stack RedoStack = new(); protected ActiveSpanState initialState; protected ActiveSpanState currentState; protected bool updatePending = false; diff --git a/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameConflictTag.cs b/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameConflictTag.cs index ad9beefdab3..892c6e7ec1e 100644 --- a/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameConflictTag.cs +++ b/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameConflictTag.cs @@ -11,7 +11,7 @@ internal sealed class RenameConflictTag : TextMarkerTag // Only used for theming, does not need localized internal const string TagId = "RoslynRenameConflictTag"; - public static readonly RenameConflictTag Instance = new RenameConflictTag(); + public static readonly RenameConflictTag Instance = new(); private RenameConflictTag() : base(TagId) diff --git a/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameFieldBackgroundAndBorderTag.cs b/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameFieldBackgroundAndBorderTag.cs index f5c9069b1fc..43a4a17fad0 100644 --- a/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameFieldBackgroundAndBorderTag.cs +++ b/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameFieldBackgroundAndBorderTag.cs @@ -11,7 +11,7 @@ internal sealed class RenameFieldBackgroundAndBorderTag : TextMarkerTag // Only used for theming, does not need localized internal const string TagId = "RoslynRenameFieldBackgroundAndBorderTag"; - public static readonly RenameFieldBackgroundAndBorderTag Instance = new RenameFieldBackgroundAndBorderTag(); + public static readonly RenameFieldBackgroundAndBorderTag Instance = new(); private RenameFieldBackgroundAndBorderTag() : base(TagId) diff --git a/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameFixupTag.cs b/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameFixupTag.cs index aaca154fea4..80f29d7b20c 100644 --- a/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameFixupTag.cs +++ b/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameFixupTag.cs @@ -11,7 +11,7 @@ internal sealed class RenameFixupTag : TextMarkerTag // Only used for theming, does not need localized internal const string TagId = "RoslynRenameFixupTag"; - public static readonly RenameFixupTag Instance = new RenameFixupTag(); + public static readonly RenameFixupTag Instance = new(); private RenameFixupTag() : base(TagId) diff --git a/src/roslyn/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs b/src/roslyn/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs index cea4219fd87..115dcac76a9 100644 --- a/src/roslyn/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs +++ b/src/roslyn/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs @@ -163,8 +163,8 @@ private void RaiseSpansChanged() internal IEnumerable GetRenameTrackingSpans() => _referenceSpanToLinkedRenameSpanMap.Values.Where(r => r.Type != RenameSpanKind.None).Concat(_conflictResolutionRenameTrackingSpans); - internal IEnumerable GetEditableSpansForSnapshot(ITextSnapshot snapshot) - => _referenceSpanToLinkedRenameSpanMap.Values.Where(r => r.Type != RenameSpanKind.None).Select(r => r.TrackingSpan.GetSpan(snapshot)); + internal ImmutableArray GetEditableSpansForSnapshot(ITextSnapshot snapshot) + => _referenceSpanToLinkedRenameSpanMap.Values.SelectAsArray(r => r.Type != RenameSpanKind.None, r => r.TrackingSpan.GetSpan(snapshot)); internal void SetReferenceSpans(IEnumerable spans) { @@ -281,7 +281,7 @@ internal void ApplyReplacementText(bool updateSelection = true) _session.UndoManager.ApplyCurrentState( _subjectBuffer, s_propagateSpansEditTag, - _referenceSpanToLinkedRenameSpanMap.Values.Where(r => r.Type != RenameSpanKind.None).Select(r => r.TrackingSpan)); + _referenceSpanToLinkedRenameSpanMap.Values.SelectAsArray(r => r.Type != RenameSpanKind.None, r => r.TrackingSpan)); if (updateSelection && _activeSpan.HasValue && this.ActiveTextView != null) { diff --git a/src/roslyn/src/EditorFeatures/Core/InlineRename/UI/InlineRenameAdornmentManager.cs b/src/roslyn/src/EditorFeatures/Core/InlineRename/UI/InlineRenameAdornmentManager.cs index 527c1415118..05c37286b62 100644 --- a/src/roslyn/src/EditorFeatures/Core/InlineRename/UI/InlineRenameAdornmentManager.cs +++ b/src/roslyn/src/EditorFeatures/Core/InlineRename/UI/InlineRenameAdornmentManager.cs @@ -32,8 +32,7 @@ internal sealed class InlineRenameAdornmentManager : IDisposable private readonly IAdornmentLayer _adornmentLayer; - private static readonly ConditionalWeakTable s_createdViewModels = - new ConditionalWeakTable(); + private static readonly ConditionalWeakTable s_createdViewModels = new(); public InlineRenameAdornmentManager( InlineRenameService renameService, diff --git a/src/roslyn/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/FilterSet.cs b/src/roslyn/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/FilterSet.cs index 1ad77dd9e78..f6a4e288eca 100644 --- a/src/roslyn/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/FilterSet.cs +++ b/src/roslyn/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/FilterSet.cs @@ -38,7 +38,7 @@ internal sealed class FilterSet(bool supportExpander) // to create a filter list covering a completion session. private static readonly ImmutableArray s_filters; - private BitVector32 _vector = new BitVector32(); + private BitVector32 _vector = new(); private static readonly int s_expanderMask; private readonly bool _supportExpander = supportExpander; diff --git a/src/roslyn/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManagerProvider.cs b/src/roslyn/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManagerProvider.cs index 1448aeb9c27..e30be495f55 100644 --- a/src/roslyn/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManagerProvider.cs +++ b/src/roslyn/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManagerProvider.cs @@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncComplet [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed class ItemManagerProvider(RecentItemsManager recentItemsManager, EditorOptionsService editorOptionsService) : IAsyncCompletionItemManagerProvider { - private readonly ItemManager _instance = new ItemManager(recentItemsManager, editorOptionsService); + private readonly ItemManager _instance = new(recentItemsManager, editorOptionsService); public IAsyncCompletionItemManager? GetOrCreate(ITextView textView) { diff --git a/src/roslyn/src/EditorFeatures/Core/Interactive/InertClassifierProvider.cs b/src/roslyn/src/EditorFeatures/Core/Interactive/InertClassifierProvider.cs index 2a4a36e2ef6..724310e9611 100644 --- a/src/roslyn/src/EditorFeatures/Core/Interactive/InertClassifierProvider.cs +++ b/src/roslyn/src/EditorFeatures/Core/Interactive/InertClassifierProvider.cs @@ -24,7 +24,7 @@ namespace Microsoft.CodeAnalysis.Interactive; [TextViewRole(PredefinedInteractiveTextViewRoles.InteractiveTextViewRole)] internal sealed partial class InertClassifierProvider : IClassifierProvider { - private static readonly object s_classificationsKey = new object(); + private static readonly object s_classificationsKey = new(); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] diff --git a/src/roslyn/src/EditorFeatures/Core/NavigateTo/DefaultNavigateToPreviewServiceFactory.cs b/src/roslyn/src/EditorFeatures/Core/NavigateTo/DefaultNavigateToPreviewServiceFactory.cs index f9a8f3a4600..854c840c821 100644 --- a/src/roslyn/src/EditorFeatures/Core/NavigateTo/DefaultNavigateToPreviewServiceFactory.cs +++ b/src/roslyn/src/EditorFeatures/Core/NavigateTo/DefaultNavigateToPreviewServiceFactory.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.NavigateTo; internal sealed class DefaultNavigateToPreviewServiceFactory : IWorkspaceServiceFactory { private readonly Lazy _singleton = - new Lazy(() => new DefaultNavigateToPreviewService()); + new(() => new DefaultNavigateToPreviewService()); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] diff --git a/src/roslyn/src/EditorFeatures/Core/NavigateTo/NavigateToItemDisplay.cs b/src/roslyn/src/EditorFeatures/Core/NavigateTo/NavigateToItemDisplay.cs index 4e918d3b822..ef1a21e22c4 100644 --- a/src/roslyn/src/EditorFeatures/Core/NavigateTo/NavigateToItemDisplay.cs +++ b/src/roslyn/src/EditorFeatures/Core/NavigateTo/NavigateToItemDisplay.cs @@ -65,12 +65,12 @@ private ReadOnlyCollection CreateDescriptionItems() var items = new List { - new DescriptionItem( + new( new ReadOnlyCollection( new[] { new DescriptionRun("Project:", bold: true) }), new ReadOnlyCollection( new[] { new DescriptionRun(document.Project.Name) })), - new DescriptionItem( + new( new ReadOnlyCollection( new[] { new DescriptionRun("File:", bold: true) }), new ReadOnlyCollection( diff --git a/src/roslyn/src/EditorFeatures/Core/NavigationBar/NavigationBarController.cs b/src/roslyn/src/EditorFeatures/Core/NavigationBar/NavigationBarController.cs index a99ad91eb46..1c5a1a0ad9f 100644 --- a/src/roslyn/src/EditorFeatures/Core/NavigationBar/NavigationBarController.cs +++ b/src/roslyn/src/EditorFeatures/Core/NavigationBar/NavigationBarController.cs @@ -195,7 +195,7 @@ void IDisposable.Dispose() _cancellationTokenSource.Cancel(); } - public TestAccessor GetTestAccessor() => new TestAccessor(this); + public TestAccessor GetTestAccessor() => new(this); private void OnEventSourceChanged(object? sender, TaggerEventArgs e) { diff --git a/src/roslyn/src/EditorFeatures/Core/Notification/EditorNotificationServiceFactory.cs b/src/roslyn/src/EditorFeatures/Core/Notification/EditorNotificationServiceFactory.cs index 7447f9e4611..79debbd5a4e 100644 --- a/src/roslyn/src/EditorFeatures/Core/Notification/EditorNotificationServiceFactory.cs +++ b/src/roslyn/src/EditorFeatures/Core/Notification/EditorNotificationServiceFactory.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Notification; [Shared] internal sealed class EditorNotificationServiceFactory : IWorkspaceServiceFactory { - private static readonly object s_gate = new object(); + private static readonly object s_gate = new(); private static EditorDialogService s_singleton; diff --git a/src/roslyn/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.UndoPrimitive.cs b/src/roslyn/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.UndoPrimitive.cs index acfddd3000d..43ce1af2c89 100644 --- a/src/roslyn/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.UndoPrimitive.cs +++ b/src/roslyn/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.UndoPrimitive.cs @@ -21,7 +21,7 @@ internal sealed partial class RenameTrackingTaggerProvider /// private sealed class UndoPrimitive(ITextBuffer textBuffer, int trackingSessionId, bool shouldRestoreStateOnUndo) : ITextUndoPrimitive { - private readonly WeakReference _weakTextBuffer = new WeakReference(textBuffer); + private readonly WeakReference _weakTextBuffer = new(textBuffer); private readonly int _trackingSessionId = trackingSessionId; private readonly bool _shouldRestoreStateOnUndo = shouldRestoreStateOnUndo; diff --git a/src/roslyn/src/EditorFeatures/Core/Shared/Extensions/HostWorkspaceServicesExtensions.cs b/src/roslyn/src/EditorFeatures/Core/Shared/Extensions/HostWorkspaceServicesExtensions.cs index 2eed1c7d183..34782c9caff 100644 --- a/src/roslyn/src/EditorFeatures/Core/Shared/Extensions/HostWorkspaceServicesExtensions.cs +++ b/src/roslyn/src/EditorFeatures/Core/Shared/Extensions/HostWorkspaceServicesExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis.Host; @@ -11,6 +12,7 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Utilities; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Shared.Extensions; @@ -83,7 +85,7 @@ private static Dictionary CreateContentTypeMap(SolutionServices l => hostWorkspaceServices.GetLanguageServices(l).GetRequiredService().GetDefaultContentType().TypeName); } - internal static IList SelectMatchingExtensionValues( + internal static ImmutableArray SelectMatchingExtensionValues( this SolutionServices workspaceServices, IEnumerable> items, IContentType contentType) @@ -92,7 +94,9 @@ internal static IList SelectMatchingExtensionValues( if (items == null) return []; - return [.. items.Where(lazy => LanguageMatches(lazy.Metadata.Language, contentType, workspaceServices)).Select(lazy => lazy.Value)]; + return items.SelectAsArray( + lazy => LanguageMatches(lazy.Metadata.Language, contentType, workspaceServices), + lazy => lazy.Value); } private static bool LanguageMatches( diff --git a/src/roslyn/src/EditorFeatures/Core/SignatureHelp/Presentation/Signature.cs b/src/roslyn/src/EditorFeatures/Core/SignatureHelp/Presentation/Signature.cs index 7beba24fb50..13a63042dc4 100644 --- a/src/roslyn/src/EditorFeatures/Core/SignatureHelp/Presentation/Signature.cs +++ b/src/roslyn/src/EditorFeatures/Core/SignatureHelp/Presentation/Signature.cs @@ -217,7 +217,7 @@ private static IList AddOptionalBrackets(bool isOptional, IList { - new TaggedText(TextTags.Punctuation, "[") + new(TextTags.Punctuation, "[") }; result.AddRange(list); result.Add(new TaggedText(TextTags.Punctuation, "]")); diff --git a/src/roslyn/src/EditorFeatures/Core/SplitComment/SplitCommentOptionsStorage.cs b/src/roslyn/src/EditorFeatures/Core/SplitComment/SplitCommentOptionsStorage.cs index b26c393f1f3..3a633de5891 100644 --- a/src/roslyn/src/EditorFeatures/Core/SplitComment/SplitCommentOptionsStorage.cs +++ b/src/roslyn/src/EditorFeatures/Core/SplitComment/SplitCommentOptionsStorage.cs @@ -9,5 +9,5 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.SplitComment; internal sealed class SplitCommentOptionsStorage { public static PerLanguageOption2 Enabled = - new PerLanguageOption2("dotnet_split_comments", defaultValue: true); + new("dotnet_split_comments", defaultValue: true); } diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/Copilot/FlavoredSuggestedAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/Copilot/FlavoredSuggestedAction.cs deleted file mode 100644 index ecef4935b1d..00000000000 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/Copilot/FlavoredSuggestedAction.cs +++ /dev/null @@ -1,50 +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. - -#nullable disable - -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.VisualStudio.Text; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; - -internal partial class SuggestedActionWithNestedFlavors -{ - /// - /// Suggested action for additional custom hyperlink in the lightbulb preview pane. - /// Each can define custom - /// code actions for adding these custom hyperlinks to the preview, - /// similar to 'Preview Changes' and 'Fix All' hyperlinks that show up for all suggested actions. - /// Note that this suggested action type just wraps the underlying original code action that comes from - /// and gets added to the suggested action set - /// holding all the suggested actions for custom hyperlinks to show in the lightbulb preview pane. - /// - protected sealed class FlavoredSuggestedAction : SuggestedAction - { - private FlavoredSuggestedAction( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - Solution originalSolution, - ITextBuffer subjectBuffer, - object provider, - CodeAction originalCodeAction) - : base(threadingContext, sourceProvider, workspace, originalSolution, subjectBuffer, provider, originalCodeAction) - { - } - - public static SuggestedAction Create(SuggestedActionWithNestedFlavors suggestedAction, CodeAction codeAction) - { - return new FlavoredSuggestedAction( - suggestedAction.ThreadingContext, - suggestedAction.SourceProvider, - suggestedAction.Workspace, - suggestedAction.OriginalSolution, - suggestedAction.SubjectBuffer, - suggestedAction.Provider, - codeAction); - } - } -} diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/FixAll/FixMultipleOccurrencesService.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/FixAll/FixMultipleOccurrencesService.cs index c1c6a26dbef..0572cf67963 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/FixAll/FixMultipleOccurrencesService.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/FixAll/FixMultipleOccurrencesService.cs @@ -9,15 +9,15 @@ using System.Composition; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.Extensions; using Microsoft.CodeAnalysis.Host.Mef; namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; /// -/// Service to compute and apply code fixes. +/// Service to compute and apply code fixes. /// [ExportWorkspaceService(typeof(IFixMultipleOccurrencesService), ServiceLayer.Host), Shared] [method: ImportingConstructor] @@ -68,7 +68,8 @@ private static async Task GetFixedSolutionAsync( IProgress progress, CancellationToken cancellationToken) { - var fixMultipleCodeAction = new FixMultipleCodeAction(fixAllState, title, waitDialogMessage); + var fixMultipleCodeAction = new RefactorOrFixAllCodeAction( + fixAllState, showPreviewChangesDialog: false, title, waitDialogMessage); Solution newSolution = null; var extensionManager = workspace.Services.GetService(); diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/PreviewChanges/PreviewChangesCodeAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/PreviewChanges/PreviewChangesCodeAction.cs index 4154ce6be95..ef2d8ba5049 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/PreviewChanges/PreviewChangesCodeAction.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/PreviewChanges/PreviewChangesCodeAction.cs @@ -11,60 +11,51 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; -internal partial class SuggestedActionWithNestedFlavors +internal sealed partial class EditorSuggestedActionWithNestedFlavors { - private partial class PreviewChangesSuggestedAction + private sealed class PreviewChangesCodeAction( + CodeAction originalCodeAction, + Func> getPreviewResultAsync) : CodeAction { - private sealed class PreviewChangesCodeAction : CodeAction - { - private readonly Workspace _workspace; - private readonly CodeAction _originalCodeAction; - private readonly Func> _getPreviewResultAsync; + private readonly CodeAction _originalCodeAction = originalCodeAction; + private readonly Func> _getPreviewResultAsync = getPreviewResultAsync; + + public override string Title => EditorFeaturesResources.Preview_changes2; - public PreviewChangesCodeAction(Workspace workspace, CodeAction originalCodeAction, Func> getPreviewResultAsync) + private protected override async Task> GetOperationsCoreAsync( + Solution originalSolution, IProgress progressTracker, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + var previewDialogService = originalSolution.Services.GetService(); + if (previewDialogService == null) { - _workspace = workspace; - _originalCodeAction = originalCodeAction; - _getPreviewResultAsync = getPreviewResultAsync; + return []; } - public override string Title => EditorFeaturesResources.Preview_changes2; - - private protected override async Task> GetOperationsCoreAsync( - Solution originalSolution, IProgress progressTracker, CancellationToken cancellationToken) + var previewResult = await _getPreviewResultAsync(cancellationToken).ConfigureAwait(true); + if (previewResult?.ChangeSummary is not { } changeSummary) { - cancellationToken.ThrowIfCancellationRequested(); - var previewDialogService = _workspace.Services.GetService(); - if (previewDialogService == null) - { - return []; - } - - var previewResult = await _getPreviewResultAsync(cancellationToken).ConfigureAwait(true); - if (previewResult?.ChangeSummary is not { } changeSummary) - { - return []; - } - - var changedSolution = previewDialogService.PreviewChanges( - EditorFeaturesResources.Preview_Changes, - "vs.codefix.previewchanges", - _originalCodeAction.Title, - EditorFeaturesResources.Changes, - CodeAnalysis.Glyph.OpenFolder, - changeSummary.NewSolution, - changeSummary.OldSolution, - showCheckBoxes: false); - - if (changedSolution == null) - { - // User pressed the cancel button. - return []; - } + return []; + } - cancellationToken.ThrowIfCancellationRequested(); - return await _originalCodeAction.GetOperationsAsync(originalSolution, progressTracker, cancellationToken).ConfigureAwait(false); + var changedSolution = previewDialogService.PreviewChanges( + EditorFeaturesResources.Preview_Changes, + "vs.codefix.previewchanges", + _originalCodeAction.Title, + EditorFeaturesResources.Changes, + CodeAnalysis.Glyph.OpenFolder, + changeSummary.NewSolution, + changeSummary.OldSolution, + showCheckBoxes: false); + + if (changedSolution == null) + { + // User pressed the cancel button. + return []; } + + cancellationToken.ThrowIfCancellationRequested(); + return await _originalCodeAction.GetOperationsAsync(originalSolution, progressTracker, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/PreviewChanges/PreviewChangesSuggestedAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/PreviewChanges/PreviewChangesSuggestedAction.cs deleted file mode 100644 index 779754b55e8..00000000000 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/PreviewChanges/PreviewChangesSuggestedAction.cs +++ /dev/null @@ -1,45 +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. - -#nullable disable - -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.VisualStudio.Text; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; - -internal partial class SuggestedActionWithNestedFlavors -{ - /// - /// Suggested action for showing the preview-changes dialog. Note: this is only used - /// as a 'flavor' inside CodeFixSuggestionAction and CodeRefactoringSuggestedAction. - /// - private sealed partial class PreviewChangesSuggestedAction : SuggestedAction - { - private PreviewChangesSuggestedAction( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - Solution originalSolution, - ITextBuffer subjectBuffer, - object provider, - PreviewChangesCodeAction codeAction) - : base(threadingContext, sourceProvider, workspace, originalSolution, subjectBuffer, provider, codeAction) - { - } - - public static SuggestedAction Create(SuggestedActionWithNestedFlavors suggestedAction) - { - return new PreviewChangesSuggestedAction( - suggestedAction.ThreadingContext, - suggestedAction.SourceProvider, - suggestedAction.Workspace, - suggestedAction.OriginalSolution, - suggestedAction.SubjectBuffer, - suggestedAction.Provider, - new PreviewChangesCodeAction( - suggestedAction.Workspace, suggestedAction.CodeAction, suggestedAction.GetPreviewResultAsync)); - } - } -} diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/RefineUsingCopilot/RefineUsingCopilotCodeAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/RefineUsingCopilot/RefineUsingCopilotCodeAction.cs index 79048bfa206..e53eef6742d 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/RefineUsingCopilot/RefineUsingCopilotCodeAction.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/RefineUsingCopilot/RefineUsingCopilotCodeAction.cs @@ -11,100 +11,91 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Copilot; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; -internal partial class SuggestedActionWithNestedFlavors +internal partial class EditorSuggestedActionWithNestedFlavors { - private partial class RefineUsingCopilotSuggestedAction + /// + /// Code action that triggers Copilot refinement session to add further + /// code changes on top of the changes from the wrapped . + /// + private sealed class RefineUsingCopilotCodeAction( + Solution originalSolution, + CodeAction originalCodeAction, + Diagnostic? primaryDiagnostic, + ICopilotCodeAnalysisService copilotCodeAnalysisService) : CodeAction { - /// - /// Code action that triggers Copilot refinement session to add further - /// code changes on top of the changes from the wrapped . - /// - private sealed class RefineUsingCopilotCodeAction( - Solution originalSolution, - CodeAction originalCodeAction, - DiagnosticData? primaryDiagnostic, - ICopilotCodeAnalysisService copilotCodeAnalysisService) : CodeAction - { - public override string Title => EditorFeaturesResources.Refine_using_Copilot; + public override string Title => EditorFeaturesResources.Refine_using_Copilot; - protected override Task> ComputePreviewOperationsAsync(CancellationToken cancellationToken) - { - // Make sure we don't trigger the refinement session for preview operation - return Task.FromResult(SpecializedCollections.EmptyEnumerable()); - } - - protected override async Task> ComputeOperationsAsync(IProgress progress, CancellationToken cancellationToken) - { - // This method is called when the user has clicked on the 'Refine using Copilot' - // hyperlink in the lightbulb preview. - // We want to bring up Copilot refinement session on top of the code changes - // from the underlying code action. Additionally, if the underlying code action - // came from a prior Copilot code fix suggestion, we also want to pass in the Copilot - // diagnostic to the refinement session. This diagnostic would be mapped to the prior - // Copilot session id to ensure that the Copilot refinement session has the historical - // context on the Copilot conversation that produce the underlying diagnostic/code action. - // - // We have a bunch of checks upfront before we bring up the Copilot refinement: - // - Applying the underlying code action produces a non-null newSolution. - // - The underlying code action produces change(s) to exactly one source document. - // - // TODO: Currently, we start a task to spawn a new Copilot refinement session - // at the end of this method, without waiting for the refinement session to complete. - // Consider if there could be better UX/platform support for such flavored actions - // where clicking on the hyperlink needs to bring up another unrelated UI. + protected override Task> ComputePreviewOperationsAsync(CancellationToken cancellationToken) + { + // Make sure we don't trigger the refinement session for preview operation + return Task.FromResult(SpecializedCollections.EmptyEnumerable()); + } - var newSolution = await originalCodeAction.GetChangedSolutionInternalAsync(originalSolution, progress, cancellationToken).ConfigureAwait(false); - if (newSolution == null) - return []; + protected override async Task> ComputeOperationsAsync(IProgress progress, CancellationToken cancellationToken) + { + // This method is called when the user has clicked on the 'Refine using Copilot' + // hyperlink in the lightbulb preview. + // We want to bring up Copilot refinement session on top of the code changes + // from the underlying code action. Additionally, if the underlying code action + // came from a prior Copilot code fix suggestion, we also want to pass in the Copilot + // diagnostic to the refinement session. This diagnostic would be mapped to the prior + // Copilot session id to ensure that the Copilot refinement session has the historical + // context on the Copilot conversation that produce the underlying diagnostic/code action. + // + // We have a bunch of checks upfront before we bring up the Copilot refinement: + // - Applying the underlying code action produces a non-null newSolution. + // - The underlying code action produces change(s) to exactly one source document. + // + // TODO: Currently, we start a task to spawn a new Copilot refinement session + // at the end of this method, without waiting for the refinement session to complete. + // Consider if there could be better UX/platform support for such flavored actions + // where clicking on the hyperlink needs to bring up another unrelated UI. - var changes = newSolution.GetChanges(originalSolution); - var changeSummary = new SolutionChangeSummary(originalSolution, newSolution, changes); - if (changeSummary.TotalFilesAffected != 1 - || changeSummary.TotalProjectsAffected != 1 - || changeSummary.NewSolution.GetChangedDocuments(changeSummary.OldSolution).FirstOrDefault() is not { } changedDocumentId) - { - return []; - } + var newSolution = await originalCodeAction.GetChangedSolutionInternalAsync(originalSolution, progress, cancellationToken).ConfigureAwait(false); + if (newSolution == null) + return []; - var oldDocument = changeSummary.OldSolution.GetRequiredDocument(changedDocumentId); - var newDocument = changeSummary.NewSolution.GetRequiredDocument(changedDocumentId); + var changes = newSolution.GetChanges(originalSolution); + var changeSummary = new SolutionChangeSummary(originalSolution, newSolution, changes); + if (changeSummary.TotalFilesAffected != 1 + || changeSummary.TotalProjectsAffected != 1 + || changeSummary.NewSolution.GetChangedDocuments(changeSummary.OldSolution).FirstOrDefault() is not { } changedDocumentId) + { + return []; + } - var convertedPrimaryDiagnostic = primaryDiagnostic != null - ? await primaryDiagnostic.ToDiagnosticAsync(oldDocument.Project, cancellationToken).ConfigureAwait(false) - : null; + var oldDocument = changeSummary.OldSolution.GetRequiredDocument(changedDocumentId); + var newDocument = changeSummary.NewSolution.GetRequiredDocument(changedDocumentId); - cancellationToken.ThrowIfCancellationRequested(); - return [new OpenRefinementSessionOperation(oldDocument, newDocument, convertedPrimaryDiagnostic, copilotCodeAnalysisService)]; - } + cancellationToken.ThrowIfCancellationRequested(); + return [new OpenRefinementSessionOperation(oldDocument, newDocument, primaryDiagnostic, copilotCodeAnalysisService)]; + } - /// - /// A code action operation for trigger Copilot Chat inline refinement session. - /// - private sealed class OpenRefinementSessionOperation( - Document oldDocument, - Document newDocument, - Diagnostic? convertedPrimaryDiagnostic, - ICopilotCodeAnalysisService copilotCodeAnalysisService) : CodeActionOperation + /// + /// A code action operation for trigger Copilot Chat inline refinement session. + /// + private sealed class OpenRefinementSessionOperation( + Document oldDocument, + Document newDocument, + Diagnostic? convertedPrimaryDiagnostic, + ICopilotCodeAnalysisService copilotCodeAnalysisService) : CodeActionOperation + { + internal override async Task TryApplyAsync(Workspace workspace, Solution originalSolution, IProgress progressTracker, CancellationToken cancellationToken) { - internal override async Task TryApplyAsync(Workspace workspace, Solution originalSolution, IProgress progressTracker, CancellationToken cancellationToken) - { - // Trigger the Copilot refinement session in background, passing in the old and new document for - // the base code changes on top of which we want to perform further refinement. - // Note that we do not pass in our cancellation token to the StartRefinementSessionAsync - // call as bringing up the refinement session is a quick operation and the refinement session - // has it's own cancellation token source to allow users to dismiss the session. - // Additionally, we do not want cancellation triggered on the token passed into - // GetChangedSolutionAsync to suddenly dismiss the refinement session UI without user explicitly - // dismissing the session. - await copilotCodeAnalysisService.StartRefinementSessionAsync(oldDocument, newDocument, convertedPrimaryDiagnostic, CancellationToken.None).ConfigureAwait(false); - return true; - } + // Trigger the Copilot refinement session in background, passing in the old and new document for + // the base code changes on top of which we want to perform further refinement. + // Note that we do not pass in our cancellation token to the StartRefinementSessionAsync + // call as bringing up the refinement session is a quick operation and the refinement session + // has it's own cancellation token source to allow users to dismiss the session. + // Additionally, we do not want cancellation triggered on the token passed into + // GetChangedSolutionAsync to suddenly dismiss the refinement session UI without user explicitly + // dismissing the session. + await copilotCodeAnalysisService.StartRefinementSessionAsync(oldDocument, newDocument, convertedPrimaryDiagnostic, CancellationToken.None).ConfigureAwait(false); + return true; } } } diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/RefineUsingCopilot/RefineUsingCopilotSuggestedAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/RefineUsingCopilot/RefineUsingCopilotSuggestedAction.cs deleted file mode 100644 index 6c11c57dfeb..00000000000 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/RefineUsingCopilot/RefineUsingCopilotSuggestedAction.cs +++ /dev/null @@ -1,70 +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.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Copilot; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.VisualStudio.Text; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; - -internal partial class SuggestedActionWithNestedFlavors -{ - /// - /// Suggested action for showing the 'Refine with Copilot' hyperlink in lightbulb preview pane. - /// Note: This hyperlink is shown for **all** suggested actions lightbulb preview, i.e. - /// regular code fixes and refactorings, in addition to Copilot suggested code fixes. - /// It wraps the core that invokes into - /// Copilot service to start a Copilot refinement session on top of the document changes - /// from the original code action corresponding to the lightbulb preview. - /// - private sealed partial class RefineUsingCopilotSuggestedAction : SuggestedAction - { - private RefineUsingCopilotSuggestedAction( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - Solution originalSolution, - ITextBuffer subjectBuffer, - object provider, - RefineUsingCopilotCodeAction codeAction) - : base(threadingContext, sourceProvider, workspace, originalSolution, subjectBuffer, provider, codeAction) - { - } - - public static async Task TryCreateAsync(SuggestedActionWithNestedFlavors suggestedAction, CancellationToken cancellationToken) - { - // NOTE: Refine with Copilot functionality is only available for code actions - // that change a source document, such that the option guarding this Refine feature - // is enabled and the Copilot service is available within this VS session. - - if (suggestedAction.OriginalDocument is not Document originalDocument) - return null; - - if (originalDocument.GetLanguageService() is not { } optionsService || - await optionsService.IsRefineOptionEnabledAsync().ConfigureAwait(false) is false) - { - return null; - } - - if (originalDocument.GetLanguageService() is not { } copilotService || - await copilotService.IsAvailableAsync(cancellationToken).ConfigureAwait(false) is false) - { - return null; - } - - return new RefineUsingCopilotSuggestedAction( - suggestedAction.ThreadingContext, - suggestedAction.SourceProvider, - suggestedAction.Workspace, - suggestedAction.OriginalSolution, - suggestedAction.SubjectBuffer, - suggestedAction.Provider, - new RefineUsingCopilotCodeAction( - suggestedAction.OriginalSolution, suggestedAction.CodeAction, suggestedAction.GetDiagnostic(), copilotService)); - } - } -} diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionPriorityProvider.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionPriorityProvider.cs deleted file mode 100644 index ab37a7d9b60..00000000000 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionPriorityProvider.cs +++ /dev/null @@ -1,51 +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.Immutable; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Diagnostics; -using Roslyn.Utilities; -using static Microsoft.CodeAnalysis.Editor.Implementation.Suggestions.SuggestedActionPriorityProvider; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; - -/// -/// Information about de-prioritized analyzers that were moved down from 'Normal' to 'Low' priority bucket. Note that this data is -/// owned by the and shared across priority buckets. -/// -internal sealed class SuggestedActionPriorityProvider( - CodeActionRequestPriority priority, - LowPriorityAnalyzersAndDiagnosticIds lowPriorityAnalyzersAndDiagnosticIds) - : ICodeActionRequestPriorityProvider -{ - public CodeActionRequestPriority? Priority { get; } = priority; - - public struct LowPriorityAnalyzersAndDiagnosticIds() - { - public ConcurrentSet Analyzers { get; } = []; - public ConcurrentSet SupportedDiagnosticIds { get; } = []; - } - - public void AddDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer) - { - lowPriorityAnalyzersAndDiagnosticIds.Analyzers.Add(analyzer); - - foreach (var supportedDiagnostic in analyzer.SupportedDiagnostics) - lowPriorityAnalyzersAndDiagnosticIds.SupportedDiagnosticIds.Add(supportedDiagnostic.Id); - } - - public bool IsDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer) - => lowPriorityAnalyzersAndDiagnosticIds.Analyzers.Contains(analyzer); - - public bool HasDeprioritizedAnalyzerSupportingDiagnosticId(ImmutableArray diagnosticIds) - { - foreach (var diagnosticId in diagnosticIds) - { - if (lowPriorityAnalyzersAndDiagnosticIds.SupportedDiagnosticIds.Contains(diagnosticId)) - return true; - } - - return false; - } -} diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/CodeFixSuggestedAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/CodeFixSuggestedAction.cs deleted file mode 100644 index 812bd157a21..00000000000 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/CodeFixSuggestedAction.cs +++ /dev/null @@ -1,52 +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. - -#nullable disable - -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; -using Microsoft.VisualStudio.Language.Intellisense; -using Microsoft.VisualStudio.Text; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; - -/// -/// Represents light bulb menu item for code fixes. -/// -internal sealed class CodeFixSuggestedAction : SuggestedActionWithNestedFlavors, ICodeFixSuggestedAction, ITelemetryDiagnosticID -{ - public CodeFix CodeFix { get; } - - public CodeFixSuggestedAction( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - TextDocument originalDocument, - ITextBuffer subjectBuffer, - CodeFix fix, - object provider, - CodeAction action, - SuggestedActionSet fixAllFlavors) - : base(threadingContext, - sourceProvider, - workspace, - originalDocument, - subjectBuffer, - provider, - action, - fixAllFlavors) - { - CodeFix = fix; - } - - public string GetDiagnosticID() - => CodeFix.PrimaryDiagnostic.GetTelemetryDiagnosticID(); - - protected override DiagnosticData GetDiagnostic() - => CodeFix.GetPrimaryDiagnosticData(); -} diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/CodeRefactoringSuggestedAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/CodeRefactoringSuggestedAction.cs deleted file mode 100644 index 2fab217d08a..00000000000 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/CodeRefactoringSuggestedAction.cs +++ /dev/null @@ -1,36 +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. - -#nullable disable - -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; -using Microsoft.VisualStudio.Language.Intellisense; -using Microsoft.VisualStudio.Text; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; - -/// -/// Represents light bulb menu item for code refactorings. -/// -internal sealed class CodeRefactoringSuggestedAction : SuggestedActionWithNestedFlavors, ICodeRefactoringSuggestedAction -{ - public CodeRefactoringProvider CodeRefactoringProvider { get; } - - public CodeRefactoringSuggestedAction( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - TextDocument originalDocument, - ITextBuffer subjectBuffer, - CodeRefactoringProvider provider, - CodeAction codeAction, - SuggestedActionSet fixAllFlavors) - : base(threadingContext, sourceProvider, workspace, originalDocument, subjectBuffer, provider, codeAction, fixAllFlavors) - { - CodeRefactoringProvider = provider; - } -} diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/SuggestedAction.CaretPositionRestorer.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedAction.CaretPositionRestorer.cs similarity index 98% rename from src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/SuggestedAction.CaretPositionRestorer.cs rename to src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedAction.CaretPositionRestorer.cs index 233fd8af6c0..be3abc31785 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/SuggestedAction.CaretPositionRestorer.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedAction.CaretPositionRestorer.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; -internal partial class SuggestedAction +internal partial class EditorSuggestedAction { internal sealed class CaretPositionRestorer : IDisposable { diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/SuggestedAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedAction.cs similarity index 82% rename from src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/SuggestedAction.cs rename to src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedAction.cs index 6c8186609cd..96d9164950b 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/SuggestedAction.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedAction.cs @@ -9,6 +9,7 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; +using EnvDTE; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; @@ -33,41 +34,25 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; /// /// Base class for all Roslyn light bulb menu items. /// -internal abstract partial class SuggestedAction : ISuggestedAction3, IEquatable +internal abstract partial class EditorSuggestedAction( + IThreadingContext threadingContext, + SuggestedActionsSourceProvider sourceProvider, + Solution originalSolution, + ITextBuffer subjectBuffer, + object provider, + CodeAction codeAction) : ISuggestedAction3, IEquatable { - protected readonly IThreadingContext ThreadingContext; - protected readonly SuggestedActionsSourceProvider SourceProvider; + protected readonly IThreadingContext ThreadingContext = threadingContext; + protected readonly SuggestedActionsSourceProvider SourceProvider = sourceProvider; - protected readonly Workspace Workspace; - protected readonly Solution OriginalSolution; - protected readonly ITextBuffer SubjectBuffer; + protected readonly Solution OriginalSolution = originalSolution; + protected readonly ITextBuffer SubjectBuffer = subjectBuffer; - protected readonly object Provider; - internal readonly CodeAction CodeAction; + protected readonly object Provider = provider; + internal CodeAction CodeAction { get; } = codeAction; private ICodeActionEditHandlerService EditHandler => SourceProvider.EditHandler; - internal SuggestedAction( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - Solution originalSolution, - ITextBuffer subjectBuffer, - object provider, - CodeAction codeAction) - { - Contract.ThrowIfNull(provider); - Contract.ThrowIfNull(codeAction); - - ThreadingContext = threadingContext; - SourceProvider = sourceProvider; - Workspace = workspace; - OriginalSolution = originalSolution; - SubjectBuffer = subjectBuffer; - Provider = provider; - CodeAction = codeAction; - } - public virtual bool TryGetTelemetryId(out Guid telemetryId) { telemetryId = CodeAction.GetTelemetryId(); @@ -118,7 +103,7 @@ private async Task InvokeAsync() { using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.SuggestedAction_Application_Summary, $"Total"); - using var token = SourceProvider.OperationListener.BeginAsyncOperation($"{nameof(SuggestedAction)}.{nameof(Invoke)}"); + using var token = SourceProvider.OperationListener.BeginAsyncOperation($"{nameof(EditorSuggestedAction)}.{nameof(Invoke)}"); using var context = SourceProvider.UIThreadOperationExecutor.BeginExecute( EditorFeaturesResources.Execute_Suggested_Action, CodeAction.Title, allowCancellation: true, showProgress: true); using var scope = context.AddScope(allowCancellation: true, CodeAction.Message); @@ -139,7 +124,7 @@ protected virtual async Task InnerInvokeAsync(IProgress pr using (new CaretPositionRestorer(SubjectBuffer, EditHandler.AssociatedViewService)) { // ConfigureAwait(true) so that CaretPositionRestorer.Dispose runs on the UI thread. - await Workspace.Services.GetService().PerformActionAsync( + await this.OriginalSolution.Services.GetService().PerformActionAsync( Provider, () => InvokeWorkerAsync(progressTracker, cancellationToken)).ConfigureAwait(true); } } @@ -176,8 +161,8 @@ private async Task InvokeWorkerAsync(IProgress progressTra var document = this.SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); await EditHandler.ApplyAsync( - Workspace, - OriginalSolution, + this.OriginalSolution.Workspace, + this.OriginalSolution, document, [.. operations], CodeAction.Title, @@ -190,11 +175,11 @@ await EditHandler.ApplyAsync( private void CreateLogProperties(Dictionary map) { // set various correlation info - if (CodeAction is AbstractFixAllCodeFixCodeAction fixSome) + if (CodeAction is RefactorOrFixAllCodeAction fixSome) { // fix all correlation info - map[FixAllLogger.CorrelationId] = fixSome.FixAllState.CorrelationId; - map[FixAllLogger.FixAllScope] = fixSome.FixAllState.Scope.ToString(); + map[FixAllLogger.CorrelationId] = fixSome.RefactorOrFixAllState.CorrelationId; + map[FixAllLogger.FixAllScope] = fixSome.RefactorOrFixAllState.Scope.ToString(); } if (TryGetTelemetryId(out var telemetryId)) @@ -203,10 +188,10 @@ private void CreateLogProperties(Dictionary map) map["TelemetryId"] = telemetryId.ToString(); } - if (this is ITelemetryDiagnosticID diagnosticId) + if (this is ITelemetryDiagnosticID telemetry && telemetry.GetDiagnosticID() is string diagnosticId) { // save what it is actually fixing - map["DiagnosticId"] = diagnosticId.GetDiagnosticID(); + map["DiagnosticId"] = diagnosticId; } } @@ -216,7 +201,7 @@ public string DisplayText { // Underscores will become an accelerator in the VS smart tag. So we double all // underscores so they actually get represented as an underscore in the UI. - var extensionManager = Workspace.Services.GetService(); + var extensionManager = this.OriginalSolution.Services.GetService(); var text = extensionManager.PerformFunction(Provider, () => CodeAction.Title, defaultValue: string.Empty); return text.Replace("_", "__"); } @@ -229,7 +214,8 @@ protected async Task GetPreviewResultAsync(CancellationTo var operations = await GetPreviewOperationsAsync(cancellationToken).ConfigureAwait(true); await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - return await EditHandler.GetPreviewsAsync(Workspace, operations, cancellationToken).ConfigureAwait(true); + return await EditHandler.GetPreviewsAsync( + this.OriginalSolution.Workspace, operations, cancellationToken).ConfigureAwait(true); } public virtual bool HasPreview => false; @@ -285,12 +271,12 @@ ImageMoniker ISuggestedAction.IconMoniker #region IEquatable public bool Equals(ISuggestedAction other) - => Equals(other as SuggestedAction); + => Equals(other as EditorSuggestedAction); public override bool Equals(object obj) - => Equals(obj as SuggestedAction); + => Equals(obj as EditorSuggestedAction); - internal bool Equals(SuggestedAction otherSuggestedAction) + internal bool Equals(EditorSuggestedAction otherSuggestedAction) { if (otherSuggestedAction == null) { @@ -327,4 +313,17 @@ public override int GetHashCode() } #endregion + + public static EditorSuggestedAction CreateTrivialAction( + EditorSuggestedAction action, + CodeAction codeAction) + => new TrivialSuggestedAction(action.ThreadingContext, action.SourceProvider, action.OriginalSolution, action.SubjectBuffer, action.Provider, codeAction); + + private sealed class TrivialSuggestedAction( + IThreadingContext threadingContext, + SuggestedActionsSourceProvider sourceProvider, + Solution originalSolution, + ITextBuffer subjectBuffer, + object provider, + CodeAction codeAction) : EditorSuggestedAction(threadingContext, sourceProvider, originalSolution, subjectBuffer, provider, codeAction); } diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/AbstractFixAllSuggestedAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedActionForRefactorOrFixAll.cs similarity index 62% rename from src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/AbstractFixAllSuggestedAction.cs rename to src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedActionForRefactorOrFixAll.cs index 1a3371ab12d..0d371bf07a9 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/AbstractFixAllSuggestedAction.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedActionForRefactorOrFixAll.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; @@ -16,39 +17,32 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; /// /// Suggested action for fix all occurrences for a code fix or a code refactoring. /// -internal abstract class AbstractFixAllSuggestedAction : SuggestedAction +internal sealed class EditorSuggestedActionForRefactorOrFixAll( + IThreadingContext threadingContext, + SuggestedActionsSourceProvider sourceProvider, + Solution originalSolution, + ITextBuffer subjectBuffer, + IRefactorOrFixAllState fixAllState, + CodeAction originalCodeAction, + string? diagnosticTelemetryId) + : EditorSuggestedAction(threadingContext, + sourceProvider, + originalSolution, + subjectBuffer, + fixAllState.FixAllProvider, + new RefactorOrFixAllCodeAction(fixAllState, showPreviewChangesDialog: true)), + ITelemetryDiagnosticID { - public CodeAction OriginalCodeAction { get; } + public string? GetDiagnosticID() => diagnosticTelemetryId; - public IFixAllState FixAllState { get; } - - protected AbstractFixAllSuggestedAction( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - Solution originalSolution, - ITextBuffer subjectBuffer, - IFixAllState fixAllState, - CodeAction originalCodeAction, - AbstractFixAllCodeAction fixAllCodeAction) - : base(threadingContext, - sourceProvider, - workspace, - originalSolution, - subjectBuffer, - fixAllState.FixAllProvider, - fixAllCodeAction) - { - OriginalCodeAction = originalCodeAction; - FixAllState = fixAllState; - } + internal new RefactorOrFixAllCodeAction CodeAction => (RefactorOrFixAllCodeAction)base.CodeAction; public override bool TryGetTelemetryId(out Guid telemetryId) { // We get the telemetry id for the original code action we are fixing, // not the special 'FixAllCodeAction'. that is the .CodeAction this // SuggestedAction is pointing at. - telemetryId = OriginalCodeAction.GetTelemetryId(FixAllState.Scope); + telemetryId = originalCodeAction.GetTelemetryId(fixAllState.Scope); return true; } @@ -57,7 +51,7 @@ protected override async Task InnerInvokeAsync( { await this.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - var fixAllKind = FixAllState.FixAllKind; + var fixAllKind = fixAllState.FixAllKind; var functionId = fixAllKind switch { FixAllKind.CodeFix => FunctionId.CodeFixes_FixAllOccurrencesSession, @@ -65,7 +59,7 @@ protected override async Task InnerInvokeAsync( _ => throw ExceptionUtilities.UnexpectedValue(fixAllKind) }; - using (Logger.LogBlock(functionId, FixAllLogger.CreateCorrelationLogMessage(FixAllState.CorrelationId), cancellationToken)) + using (Logger.LogBlock(functionId, FixAllLogger.CreateCorrelationLogMessage(fixAllState.CorrelationId), cancellationToken)) { await base.InnerInvokeAsync(progress, cancellationToken).ConfigureAwait(false); } diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionWithNestedActions.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedActionWithNestedActions.cs similarity index 50% rename from src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionWithNestedActions.cs rename to src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedActionWithNestedActions.cs index c5c6aee3c8b..feba7ae1499 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionWithNestedActions.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedActionWithNestedActions.cs @@ -20,50 +20,30 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; /// Lightbulb item that has child items that should be displayed as 'menu items' /// (as opposed to 'flavor items'). /// -internal sealed class SuggestedActionWithNestedActions : SuggestedAction +internal sealed class EditorSuggestedActionWithNestedActions( + IThreadingContext threadingContext, + SuggestedActionsSourceProvider sourceProvider, + Solution originalSolution, + ITextBuffer subjectBuffer, + object provider, + CodeAction codeAction, + ImmutableArray nestedActionSets) + : EditorSuggestedAction(threadingContext, sourceProvider, originalSolution, subjectBuffer, provider, codeAction) { - public readonly ImmutableArray NestedActionSets; + public readonly ImmutableArray NestedActionSets = nestedActionSets; - public SuggestedActionWithNestedActions( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - Solution originalSolution, - ITextBuffer subjectBuffer, - object provider, - CodeAction codeAction, - ImmutableArray nestedActionSets) - : base(threadingContext, sourceProvider, workspace, originalSolution, subjectBuffer, provider, codeAction) - { - Debug.Assert(!nestedActionSets.IsDefaultOrEmpty); - NestedActionSets = nestedActionSets; - } - - public SuggestedActionWithNestedActions( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - Solution originalSolution, - ITextBuffer subjectBuffer, - object provider, - CodeAction codeAction, - SuggestedActionSet nestedActionSet) - : this(threadingContext, sourceProvider, workspace, originalSolution, subjectBuffer, provider, codeAction, [nestedActionSet]) - { - } - - public override bool HasActionSets => true; + public sealed override bool HasActionSets => true; public sealed override Task> GetActionSetsAsync(CancellationToken cancellationToken) => Task.FromResult>(NestedActionSets); - protected override Task InnerInvokeAsync(IProgress progress, CancellationToken cancellationToken) + protected sealed override Task InnerInvokeAsync(IProgress progress, CancellationToken cancellationToken) { // A code action with nested actions is itself never invokable. So just do nothing if this ever gets asked. // Report a message in debug and log a watson exception so that if this is hit we can try to narrow down how // this happened. - Debug.Fail($"{nameof(InnerInvokeAsync)} should not be called on a {nameof(SuggestedActionWithNestedActions)}"); - FatalError.ReportAndCatch(new InvalidOperationException($"{nameof(InnerInvokeAsync)} should not be called on a {nameof(SuggestedActionWithNestedActions)}"), ErrorSeverity.Critical); + Debug.Fail($"{nameof(InnerInvokeAsync)} should not be called on a {nameof(EditorSuggestedActionWithNestedActions)}"); + FatalError.ReportAndCatch(new InvalidOperationException($"{nameof(InnerInvokeAsync)} should not be called on a {nameof(EditorSuggestedActionWithNestedActions)}"), ErrorSeverity.Critical); return Task.CompletedTask; } diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionWithNestedFlavors.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedActionWithNestedFlavors.cs similarity index 60% rename from src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionWithNestedFlavors.cs rename to src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedActionWithNestedFlavors.cs index 9e606d7d0e4..074bbe05c5c 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionWithNestedFlavors.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedActionWithNestedFlavors.cs @@ -2,67 +2,61 @@ // 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.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.Copilot; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Extensions; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; /// -/// Base type for all SuggestedActions that have 'flavors'. 'Flavors' are child actions that -/// are presented as simple links, not as menu-items, in the light-bulb. Examples of 'flavors' -/// include 'preview changes' (for refactorings and fixes) and 'fix all in document, project, solution' -/// (for refactorings and fixes). +/// Type for all SuggestedActions that have 'flavors'. 'Flavors' are child actions that are presented as simple links, +/// not as menu-items, in the light-bulb. Examples of 'flavors' include 'preview changes' (for refactorings and fixes) +/// and 'fix all in document, project, solution' (for refactorings and fixes). /// -/// Because all derivations support 'preview changes', we bake that logic into this base type. +/// Supports 'preview changes' for all changes. /// -internal abstract partial class SuggestedActionWithNestedFlavors : SuggestedAction, ISuggestedActionWithFlavors +internal sealed partial class EditorSuggestedActionWithNestedFlavors( + IThreadingContext threadingContext, + SuggestedActionsSourceProvider sourceProvider, + TextDocument originalDocument, + ITextBuffer subjectBuffer, + object provider, + CodeAction codeAction, + SuggestedActionSet? fixAllFlavors, + ImmutableArray diagnostics) + : EditorSuggestedAction(threadingContext, + sourceProvider, + originalDocument.Project.Solution, + subjectBuffer, + provider, + codeAction), ISuggestedActionWithFlavors, ITelemetryDiagnosticID { - private readonly SuggestedActionSet _fixAllFlavors; + private readonly SuggestedActionSet? _fixAllFlavors = fixAllFlavors; + private readonly ImmutableArray _diagnostics = diagnostics; + private ImmutableArray _nestedFlavors; - public TextDocument OriginalDocument { get; } - - public SuggestedActionWithNestedFlavors( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - TextDocument originalDocument, - ITextBuffer subjectBuffer, - object provider, - CodeAction codeAction, - SuggestedActionSet fixAllFlavors) - : base(threadingContext, - sourceProvider, - workspace, - originalDocument.Project.Solution, - subjectBuffer, - provider, - codeAction) - { - _fixAllFlavors = fixAllFlavors; - OriginalDocument = originalDocument; - } + public TextDocument OriginalDocument { get; } = originalDocument; /// /// HasActionSets is always true because we always know we provide 'preview changes'. /// public sealed override bool HasActionSets => true; - public sealed override async Task> GetActionSetsAsync(CancellationToken cancellationToken) + public sealed override async Task?> GetActionSetsAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -71,7 +65,7 @@ public sealed override async Task> GetActionSets if (_nestedFlavors.IsDefault) { - var extensionManager = this.Workspace.Services.GetService(); + var extensionManager = this.OriginalSolution.Services.GetRequiredService(); // Note: We must ensure that CreateAllFlavorsAsync does not perform any expensive // long running operations as it will be invoked when a lightbulb preview is brought @@ -89,17 +83,12 @@ public sealed override async Task> GetActionSets private async Task> CreateAllFlavorsAsync(CancellationToken cancellationToken) { - var builder = ArrayBuilder.GetInstance(); + using var _ = ArrayBuilder.GetInstance(out var builder); - var primarySuggestedActionSet = await GetPrimarySuggestedActionSetAsync(cancellationToken).ConfigureAwait(false); - builder.Add(primarySuggestedActionSet); + builder.Add(await GetPrimarySuggestedActionSetAsync(cancellationToken).ConfigureAwait(false)); + builder.AddIfNotNull(_fixAllFlavors); - if (_fixAllFlavors != null) - { - builder.Add(_fixAllFlavors); - } - - return builder.ToImmutableAndFree(); + return builder.ToImmutableAndClear(); } private async Task GetPrimarySuggestedActionSetAsync(CancellationToken cancellationToken) @@ -114,20 +103,41 @@ private async Task GetPrimarySuggestedActionSetAsync(Cancell // Note that flavored suggested actions for Fix All operations are added in a separate // suggested action set by our caller, we don't add them here. - using var _ = ArrayBuilder.GetInstance(out var suggestedActions); - var previewChangesAction = PreviewChangesSuggestedAction.Create(this); + using var _ = ArrayBuilder.GetInstance(out var suggestedActions); + var previewChangesAction = CreateTrivialAction( + this, new PreviewChangesCodeAction(this.CodeAction, this.GetPreviewResultAsync)); suggestedActions.Add(previewChangesAction); - var refineUsingCopilotAction = await RefineUsingCopilotSuggestedAction.TryCreateAsync(this, cancellationToken).ConfigureAwait(false); + var refineUsingCopilotAction = await TryCreateRefineSuggestedActionAsync().ConfigureAwait(false); if (refineUsingCopilotAction != null) suggestedActions.Add(refineUsingCopilotAction); foreach (var action in this.CodeAction.AdditionalPreviewFlavors) - { - suggestedActions.Add(FlavoredSuggestedAction.Create(this, action)); - } + suggestedActions.Add(CreateTrivialAction(this, action)); return new SuggestedActionSet(categoryName: null, actions: suggestedActions.ToImmutable()); + + async Task TryCreateRefineSuggestedActionAsync() + { + if (this.OriginalDocument is not Document originalDocument) + return null; + + if (originalDocument.GetLanguageService() is not { } optionsService || + await optionsService.IsRefineOptionEnabledAsync().ConfigureAwait(false) is false) + { + return null; + } + + if (originalDocument.GetLanguageService() is not { } copilotService || + await copilotService.IsAvailableAsync(cancellationToken).ConfigureAwait(false) is false) + { + return null; + } + + return CreateTrivialAction( + this, new RefineUsingCopilotCodeAction( + this.OriginalSolution, this.CodeAction, _diagnostics.FirstOrDefault(), copilotService)); + } } // HasPreview is called synchronously on the UI thread. In order to avoid blocking the UI thread, @@ -139,14 +149,14 @@ private async Task GetPrimarySuggestedActionSetAsync(Cancell // 'null' / empty collection from within GetPreviewAsync(). public override bool HasPreview => true; - public override async Task GetPreviewAsync(CancellationToken cancellationToken) + public override async Task GetPreviewAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // Light bulb will always invoke this function on the UI thread. this.ThreadingContext.ThrowIfNotOnUIThread(); - var previewPaneService = Workspace.Services.GetService(); + var previewPaneService = this.OriginalSolution.Services.GetService(); if (previewPaneService == null) { return null; @@ -154,10 +164,10 @@ public override async Task GetPreviewAsync(CancellationToken cancellatio // after this point, this method should only return at GetPreviewPane. otherwise, DifferenceViewer will leak // since there is no one to close the viewer - var preferredDocumentId = Workspace.GetDocumentIdInCurrentContext(SubjectBuffer.AsTextContainer()); - var preferredProjectId = preferredDocumentId?.ProjectId; + var preferredDocumentId = this.OriginalDocument.Id; + var preferredProjectId = preferredDocumentId.ProjectId; - var extensionManager = this.Workspace.Services.GetService(); + var extensionManager = this.OriginalSolution.Services.GetRequiredService(); var previewContents = await extensionManager.PerformFunctionAsync(Provider, async cancellationToken => { // We need to stay on UI thread after GetPreviewResultAsync() so that TakeNextPreviewAsync() @@ -180,8 +190,10 @@ public override async Task GetPreviewAsync(CancellationToken cancellatio // GetPreviewPane() needs to run on the UI thread. this.ThreadingContext.ThrowIfNotOnUIThread(); - return previewPaneService.GetPreviewPane(GetDiagnostic(), previewContents); + var diagnosticData = DiagnosticData.Create(_diagnostics.FirstOrDefault(), this.OriginalDocument.Project); + return previewPaneService.GetPreviewPane(diagnosticData, previewContents!); } - protected virtual DiagnosticData GetDiagnostic() => null; + public string? GetDiagnosticID() + => _diagnostics.FirstOrDefault()?.GetTelemetryDiagnosticID(); } diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.FixAllCodeAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.FixAllCodeAction.cs deleted file mode 100644 index 45a7656998b..00000000000 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.FixAllCodeAction.cs +++ /dev/null @@ -1,19 +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 Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; - -internal partial class FixAllCodeFixSuggestedAction -{ - private sealed partial class FixAllCodeAction : AbstractFixAllCodeFixCodeAction - { - public FixAllCodeAction(IFixAllState fixAllState) - : base(fixAllState, showPreviewChangesDialog: true) - { - } - } -} diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.cs deleted file mode 100644 index 519dcd53adb..00000000000 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.cs +++ /dev/null @@ -1,46 +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 Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; -using Microsoft.VisualStudio.Language.Intellisense; -using Microsoft.VisualStudio.Text; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; - -/// -/// Suggested action for fix all occurrences code fix. Note: this is only used -/// as a 'flavor' inside CodeFixSuggestionAction. -/// -internal sealed partial class FixAllCodeFixSuggestedAction : AbstractFixAllSuggestedAction, ITelemetryDiagnosticID, IFixAllCodeFixSuggestedAction -{ - public Diagnostic Diagnostic { get; } - - public FixAllCodeFixSuggestedAction( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - Solution originalSolution, - ITextBuffer subjectBuffer, - IFixAllState fixAllState, - Diagnostic diagnostic, - CodeAction originalCodeAction) - : base(threadingContext, - sourceProvider, - workspace, - originalSolution, - subjectBuffer, - fixAllState, - originalCodeAction, - new FixAllCodeAction(fixAllState)) - { - Diagnostic = diagnostic; - } - - public string GetDiagnosticID() - => Diagnostic.GetTelemetryDiagnosticID(); -} diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs deleted file mode 100644 index b8a66badd9a..00000000000 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs +++ /dev/null @@ -1,38 +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 Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; -using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; -using Microsoft.VisualStudio.Text; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; - -/// -/// Suggested action for fix all occurrences for a code refactoring. Note: this is only used -/// as a 'flavor' inside CodeRefactoringSuggestionAction. -/// -internal sealed class FixAllCodeRefactoringSuggestedAction : AbstractFixAllSuggestedAction, IFixAllCodeRefactoringSuggestedAction -{ - public FixAllCodeRefactoringSuggestedAction( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - Solution originalSolution, - ITextBuffer subjectBuffer, - IFixAllState fixAllState, - CodeAction originalCodeAction) - : base(threadingContext, - sourceProvider, - workspace, - originalSolution, - subjectBuffer, - fixAllState, - originalCodeAction, - new FixAllCodeRefactoringCodeAction(fixAllState)) - { - } -} diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSource.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSource.cs index b65af94657a..59e14821b31 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSource.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSource.cs @@ -199,33 +199,30 @@ private void OnTextViewClosed(object sender, EventArgs e) if (state is null) return null; - var lowPriorityAnalyzerData = new SuggestedActionPriorityProvider.LowPriorityAnalyzersAndDiagnosticIds(); - foreach (var order in Orderings) { var priority = TryGetPriority(order); Contract.ThrowIfNull(priority); - var priorityProvider = new SuggestedActionPriorityProvider(priority.Value, lowPriorityAnalyzerData); - var result = await GetFixCategoryAsync(priorityProvider).ConfigureAwait(false); + var result = await GetFixCategoryAsync(priority).ConfigureAwait(false); if (result != null) return result; } return null; - async Task GetFixCategoryAsync(ICodeActionRequestPriorityProvider priorityProvider) + async Task GetFixCategoryAsync(CodeActionRequestPriority? priority) { if (state.Target.Owner._codeFixService != null && state.Target.SubjectBuffer.SupportsCodeFixes()) { var result = await state.Target.Owner._codeFixService.GetMostSevereFixAsync( - document, range.Span.ToTextSpan(), priorityProvider, cancellationToken).ConfigureAwait(false); + document, range.Span.ToTextSpan(), priority, cancellationToken).ConfigureAwait(false); if (result != null) { Logger.Log(FunctionId.SuggestedActions_HasSuggestedActionsAsync); - return result.FirstDiagnostic.Severity switch + return result.Diagnostics.First().Severity switch { DiagnosticSeverity.Hidden or DiagnosticSeverity.Info or DiagnosticSeverity.Warning => PredefinedSuggestedActionCategoryNames.CodeFix, diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSourceProvider.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSourceProvider.cs index 19090e94817..42f148b097b 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSourceProvider.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSourceProvider.cs @@ -42,9 +42,9 @@ internal sealed partial class SuggestedActionsSourceProvider : ISuggestedActions { public static readonly ImmutableArray Orderings = [DefaultOrderings.Highest, DefaultOrderings.Default, DefaultOrderings.Low, DefaultOrderings.Lowest]; - private static readonly Guid s_CSharpSourceGuid = new Guid("b967fea8-e2c3-4984-87d4-71a38f49e16a"); - private static readonly Guid s_visualBasicSourceGuid = new Guid("4de30e93-3e0c-40c2-a4ba-1124da4539f6"); - private static readonly Guid s_xamlSourceGuid = new Guid("a0572245-2eab-4c39-9f61-06a6d8c5ddda"); + private static readonly Guid s_CSharpSourceGuid = new("b967fea8-e2c3-4984-87d4-71a38f49e16a"); + private static readonly Guid s_visualBasicSourceGuid = new("4de30e93-3e0c-40c2-a4ba-1124da4539f6"); + private static readonly Guid s_xamlSourceGuid = new("a0572245-2eab-4c39-9f61-06a6d8c5ddda"); private readonly IThreadingContext _threadingContext; private readonly ICodeRefactoringService _codeRefactoringService; diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSource_Async.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSource_Async.cs index 1b60d0cac2d..5a24330764a 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSource_Async.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSource_Async.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; @@ -19,10 +18,11 @@ using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; -using Microsoft.CodeAnalysis.UnifiedSuggestions; +using Microsoft.CodeAnalysis.Suggestions; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; using Roslyn.Utilities; @@ -100,16 +100,10 @@ await document.Project.Solution.Services.GetRequiredService>.GetInstance(out var pendingActionSets); + using var _ = PooledDictionary>.GetInstance(out var pendingActionSets); try { - // Keep track of the diagnostic analyzers that have been deprioritized across calls to the - // diagnostic engine. We'll run them once we get around to the low-priority bucket. We want to - // keep track of this *across* calls to each priority. So we create this set outside of the loop and - // then pass it continuously from one priority group to the next. - var lowPriorityAnalyzerData = new SuggestedActionPriorityProvider.LowPriorityAnalyzersAndDiagnosticIds(); - using var _2 = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.SuggestedAction_Summary, $"Total"); // Collectors are in priority order. So just walk them from highest to lowest. @@ -121,8 +115,7 @@ await document.Project.Solution.Services.GetRequiredService ArrayBuilder.GetInstance()); + var builder = pendingActionSets.GetOrAdd(actualSetPriority, _ => ArrayBuilder.GetInstance()); builder.Add(set); } else @@ -192,21 +185,21 @@ await document.Project.Solution.Services.GetRequiredService GetCodeFixesAndRefactoringsAsync( + private async IAsyncEnumerable GetCodeFixesAndRefactoringsAsync( ReferenceCountedDisposable state, ISuggestedActionCategorySet requestedActionCategories, TextDocument document, SnapshotSpan range, TextSpan? selection, - ICodeActionRequestPriorityProvider priorityProvider, + CodeActionRequestPriority? priority, int currentActionCount, [EnumeratorCancellation] CancellationToken cancellationToken) { var target = state.Target; var owner = target.Owner; var subjectBuffer = target.SubjectBuffer; - var workspace = document.Project.Solution.Workspace; - var supportsFeatureService = workspace.Services.GetRequiredService(); + var solution = document.Project.Solution; + var supportsFeatureService = solution.Services.GetRequiredService(); var fixesTask = GetCodeFixesAsync(); var refactoringsTask = GetRefactoringsAsync(); @@ -224,9 +217,9 @@ private async IAsyncEnumerable GetCodeFixesAndRefactoringsAs yield break; - async Task> GetCodeFixesAsync() + async Task> GetCodeFixesAsync() { - using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.SuggestedAction_Summary, $"Total.Pri{priorityProvider.Priority.GetPriorityInt()}.{nameof(GetCodeFixesAsync)}"); + using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.SuggestedAction_Summary, $"Total.Pri{priority.GetPriorityInt()}.{nameof(GetCodeFixesAsync)}"); if (owner._codeFixService == null || !supportsFeatureService.SupportsCodeFixes(target.SubjectBuffer) || @@ -236,13 +229,13 @@ async Task> GetCodeFixesAsync() } return await UnifiedSuggestedActionsSource.GetFilterAndOrderCodeFixesAsync( - workspace, owner._codeFixService, document, range.Span.ToTextSpan(), - priorityProvider, cancellationToken).ConfigureAwait(false); + owner._codeFixService, document, range.Span.ToTextSpan(), + priority, cancellationToken).ConfigureAwait(false); } - async Task> GetRefactoringsAsync() + async Task> GetRefactoringsAsync() { - using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.SuggestedAction_Summary, $"Total.Pri{priorityProvider.Priority.GetPriorityInt()}.{nameof(GetRefactoringsAsync)}"); + using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.SuggestedAction_Summary, $"Total.Pri{priority.GetPriorityInt()}.{nameof(GetRefactoringsAsync)}"); if (!selection.HasValue) { @@ -260,7 +253,7 @@ async Task> GetRefactoringsAsync() // 'CodeActionRequestPriority.Lowest' is reserved for suppression/configuration code fixes. // No code refactoring should have this request priority. - if (priorityProvider.Priority == CodeActionRequestPriority.Lowest) + if (priority == CodeActionRequestPriority.Lowest) return []; // If we are computing refactorings outside the 'Refactoring' context, i.e. for example, from the lightbulb under a squiggle or selection, @@ -268,60 +261,66 @@ async Task> GetRefactoringsAsync() var filterOutsideSelection = !requestedActionCategories.Contains(PredefinedSuggestedActionCategoryNames.Refactoring); return await UnifiedSuggestedActionsSource.GetFilterAndOrderCodeRefactoringsAsync( - workspace, owner._codeRefactoringService, document, selection.Value, priorityProvider.Priority, + owner._codeRefactoringService, document, selection.Value, priority, filterOutsideSelection, cancellationToken).ConfigureAwait(false); } - [return: NotNullIfNotNull(nameof(unifiedSuggestedActionSet))] - SuggestedActionSet? ConvertToSuggestedActionSet(UnifiedSuggestedActionSet? unifiedSuggestedActionSet, TextDocument originalDocument) + VisualStudio.Language.Intellisense.SuggestedActionSet ConvertToSuggestedActionSet(CodeAnalysis.Suggestions.SuggestedActionSet unifiedSuggestedActionSet, TextDocument originalDocument) { - // May be null in cases involving CodeFixSuggestedActions since FixAllFlavors may be null. - if (unifiedSuggestedActionSet == null) - return null; - - var originalSolution = unifiedSuggestedActionSet.OriginalSolution; - - return new SuggestedActionSet( + return new VisualStudio.Language.Intellisense.SuggestedActionSet( unifiedSuggestedActionSet.CategoryName, unifiedSuggestedActionSet.Actions.SelectAsArray(set => ConvertToSuggestedAction(set)), unifiedSuggestedActionSet.Title, ConvertToSuggestedActionSetPriority(unifiedSuggestedActionSet.Priority), unifiedSuggestedActionSet.ApplicableToSpan?.ToSpan()); - ISuggestedAction ConvertToSuggestedAction(IUnifiedSuggestedAction unifiedSuggestedAction) - => unifiedSuggestedAction switch + ISuggestedAction ConvertToSuggestedAction(SuggestedAction action) + { + if (action.RefactorOrFixAllState != null) + { + return new EditorSuggestedActionForRefactorOrFixAll( + _threadingContext, owner, originalDocument.Project.Solution, subjectBuffer, + action.RefactorOrFixAllState, action.CodeAction, + action.Diagnostics.FirstOrDefault()?.GetTelemetryDiagnosticID()); + } + else if (!action.NestedActionSets.IsEmpty) + { + return new EditorSuggestedActionWithNestedActions( + _threadingContext, owner, document.Project.Solution, subjectBuffer, + action.Provider, action.CodeAction, + action.NestedActionSets.SelectAsArray(s => ConvertToSuggestedActionSet(s, originalDocument))); + } + else { - UnifiedCodeFixSuggestedAction codeFixAction => new CodeFixSuggestedAction( - _threadingContext, owner, codeFixAction.Workspace, originalDocument, subjectBuffer, - codeFixAction.CodeFix, codeFixAction.Provider, codeFixAction.OriginalCodeAction, - ConvertToSuggestedActionSet(codeFixAction.FixAllFlavors, originalDocument)), - UnifiedCodeRefactoringSuggestedAction codeRefactoringAction => new CodeRefactoringSuggestedAction( - _threadingContext, owner, codeRefactoringAction.Workspace, originalDocument, subjectBuffer, - codeRefactoringAction.CodeRefactoringProvider, codeRefactoringAction.OriginalCodeAction, - ConvertToSuggestedActionSet(codeRefactoringAction.FixAllFlavors, originalDocument)), - UnifiedFixAllCodeFixSuggestedAction fixAllAction => new FixAllCodeFixSuggestedAction( - _threadingContext, owner, fixAllAction.Workspace, originalSolution, subjectBuffer, - fixAllAction.FixAllState, fixAllAction.Diagnostic, fixAllAction.OriginalCodeAction), - UnifiedFixAllCodeRefactoringSuggestedAction fixAllCodeRefactoringAction => new FixAllCodeRefactoringSuggestedAction( - _threadingContext, owner, fixAllCodeRefactoringAction.Workspace, originalSolution, subjectBuffer, - fixAllCodeRefactoringAction.FixAllState, fixAllCodeRefactoringAction.OriginalCodeAction), - UnifiedSuggestedActionWithNestedActions nestedAction => new SuggestedActionWithNestedActions( - _threadingContext, owner, nestedAction.Workspace, originalSolution, subjectBuffer, - nestedAction.Provider ?? this, nestedAction.OriginalCodeAction, - nestedAction.NestedActionSets.SelectAsArray(s => ConvertToSuggestedActionSet(s, originalDocument))), - _ => throw ExceptionUtilities.Unreachable() + return new EditorSuggestedActionWithNestedFlavors( + _threadingContext, owner, originalDocument, subjectBuffer, action.Provider, action.CodeAction, + ConvertFlavors(action.Flavors), action.Diagnostics); + } + } + + static SuggestedActionSetPriority ConvertToSuggestedActionSetPriority(CodeActionPriority priority) + => priority switch + { + CodeActionPriority.Lowest => SuggestedActionSetPriority.None, + CodeActionPriority.Low => SuggestedActionSetPriority.Low, + CodeActionPriority.Default => SuggestedActionSetPriority.Medium, + CodeActionPriority.High => SuggestedActionSetPriority.High, + _ => throw ExceptionUtilities.Unreachable(), }; - } - static SuggestedActionSetPriority ConvertToSuggestedActionSetPriority(CodeActionPriority priority) - => priority switch + VisualStudio.Language.Intellisense.SuggestedActionSet? ConvertFlavors(SuggestedActionFlavors? flavors) { - CodeActionPriority.Lowest => SuggestedActionSetPriority.None, - CodeActionPriority.Low => SuggestedActionSetPriority.Low, - CodeActionPriority.Default => SuggestedActionSetPriority.Medium, - CodeActionPriority.High => SuggestedActionSetPriority.High, - _ => throw ExceptionUtilities.Unreachable(), - }; + if (flavors is null) + return null; + + return new( + categoryName: null, + actions: flavors.Value.Actions.SelectAsArray(a => ConvertToSuggestedAction(a)), + title: flavors.Value.Title, + priority: SuggestedActionSetPriority.Low, + applicableToSpan: null); + } + } } } } diff --git a/src/roslyn/src/EditorFeatures/Core/Utilities/NativeMethods.cs b/src/roslyn/src/EditorFeatures/Core/Utilities/NativeMethods.cs index a5560e5bb39..3647ecc70b0 100644 --- a/src/roslyn/src/EditorFeatures/Core/Utilities/NativeMethods.cs +++ b/src/roslyn/src/EditorFeatures/Core/Utilities/NativeMethods.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Editor.Wpf.Utilities; internal static class NativeMethods { - public static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF); + public static readonly IntPtr HWND_BROADCAST = new(0xFFFF); public const int WM_SYSCOLORCHANGE = 0x0015; [DllImport("user32.dll", CharSet = CharSet.Auto)] diff --git a/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/AbstractChangeSignatureTests.cs b/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/AbstractChangeSignatureTests.cs index 6d5705ade3c..5290e20b9b9 100644 --- a/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/AbstractChangeSignatureTests.cs +++ b/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/AbstractChangeSignatureTests.cs @@ -160,7 +160,7 @@ private static string CreateDiagnosticsString(ImmutableArray diagnos private static string GetSignatureDescriptionString(Document document, AddedParameterOrExistingIndex[] signature, int? totalParameters) { - var existingParametersKept = signature.Where(p => p.IsExisting).Select(p => p.OldIndex).ToArray(); + var existingParametersKept = signature.SelectAsArray(p => p.IsExisting, p => p.OldIndex); var removeDescription = string.Empty; if (totalParameters.HasValue) { @@ -176,7 +176,7 @@ private static string GetSignatureDescriptionString(Document document, AddedPara removeDescription = removed.Any() ? string.Format(", Removed: {{{0}}}", string.Join(", ", removed)) : string.Empty; } - var newParametersString = string.Join(",", signature.Where(p => !p.IsExisting).Select(p => p.GetAddedParameter(document))); + var newParametersString = string.Join(",", signature.SelectAsArray(p => !p.IsExisting, p => p.GetAddedParameter(document))); var addDescription = !newParametersString.IsEmpty() ? string.Format(", Added {{{0}}}", newParametersString) : string.Empty; return string.Format("Parameters: <{0}>{1}{2}", string.Join(", ", signature.Select(item => item.ToString())), removeDescription, addDescription); diff --git a/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionTest.cs b/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionTest.cs index 142dad1e5a0..d6e10763eea 100644 --- a/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionTest.cs +++ b/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionTest.cs @@ -61,7 +61,7 @@ protected abstract CodeRefactoringProvider CreateCodeRefactoringProvider( return (actions, actionToInvoke); var fixAllCodeAction = await GetFixAllFixAsync(actionToInvoke, - refactoring.Provider, document, span, fixAllScope.Value).ConfigureAwait(false); + refactoring.Provider, document, span, fixAllScope.Value.ToRefactorAllScope()).ConfigureAwait(false); if (fixAllCodeAction == null) return ([], null); @@ -73,15 +73,15 @@ private static async Task GetFixAllFixAsync( CodeRefactoringProvider provider, Document document, TextSpan selectionSpan, - FixAllScope scope) + RefactorAllScope scope) { - var fixAllProvider = provider.GetFixAllProvider(); - if (fixAllProvider == null || !fixAllProvider.GetSupportedFixAllScopes().Contains(scope)) + var refactorAllProvider = provider.GetRefactorAllProvider(); + if (refactorAllProvider == null || !refactorAllProvider.GetSupportedRefactorAllScopes().Contains(scope)) return null; - var fixAllState = new FixAllState(fixAllProvider, document, selectionSpan, provider, scope, originalCodeAction); - var fixAllContext = new FixAllContext(fixAllState, CodeAnalysisProgress.None, CancellationToken.None); - return await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false); + var refactorAllState = new RefactorAllState(refactorAllProvider, document, selectionSpan, provider, scope, originalCodeAction); + var refactorAllContext = new RefactorAllContext(refactorAllState, CodeAnalysisProgress.None, CancellationToken.None); + return await refactorAllProvider.GetRefactoringAsync(refactorAllContext).ConfigureAwait(false); } protected override Task> GetDiagnosticsWorkerAsync(EditorTestWorkspace workspace, TestParameters parameters) diff --git a/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs b/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs index aaef5679e0b..88628f213a6 100644 --- a/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs +++ b/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs @@ -157,7 +157,7 @@ protected static Document GetDocumentAndSelectSpan(EditorTestWorkspace workspace document, diagnostic.Location.SourceSpan, [diagnostic], - (a, d) => fixes.Add(new CodeFix(document.Project, a, d)), + (a, d) => fixes.Add(new CodeFix(a, d)), CancellationToken.None); await fixer.RegisterCodeFixesAsync(context); @@ -256,7 +256,7 @@ internal async Task TestSpansAsync( else { var diagnostics = await GetDiagnosticsAsync(workspace, ps); - actualTextSpans = diagnostics.Where(d => d.Id == diagnosticId).Select(d => d.Location.SourceSpan).ToSet(); + actualTextSpans = diagnostics.SelectAsArray(d => d.Id == diagnosticId, d => d.Location.SourceSpan).ToSet(); } Assert.True(expectedTextSpans.SetEquals(actualTextSpans)); diff --git a/src/roslyn/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/roslyn/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index 720a63cd339..b828c08c2ff 100644 --- a/src/roslyn/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/roslyn/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -58,7 +58,7 @@ public async Task TestGetFirstDiagnosticWithFixAsync() var project = workspace.CurrentSolution.Projects.Single().AddAnalyzerReference(reference); var document = project.Documents.Single(); var unused = await fixService.GetMostSevereFixAsync( - document, TextSpan.FromBounds(0, 0), new DefaultCodeActionRequestPriorityProvider(), CancellationToken.None); + document, TextSpan.FromBounds(0, 0), priority: null, CancellationToken.None); var fixer1 = (MockFixer)fixers.Single().Value; var fixer2 = (MockFixer)reference.Fixers.Single(); @@ -142,14 +142,14 @@ public async Task TestGetFixesAsyncForFixableAndNonFixableAnalyzersAsync() // Verify only analyzerWithFix is executed when GetFixesAsync is invoked with 'CodeActionRequestPriority.Normal'. _ = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), - priorityProvider: new DefaultCodeActionRequestPriorityProvider(CodeActionRequestPriority.Default), + CodeActionRequestPriority.Default, cancellationToken: CancellationToken.None); Assert.True(analyzerWithFix.ReceivedCallback); Assert.False(analyzerWithoutFix.ReceivedCallback); // Verify both analyzerWithFix and analyzerWithoutFix are executed when GetFixesAsync is invoked with 'CodeActionRequestPriority.Lowest'. _ = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), - priorityProvider: new DefaultCodeActionRequestPriorityProvider(CodeActionRequestPriority.Lowest), + CodeActionRequestPriority.Lowest, cancellationToken: CancellationToken.None); Assert.True(analyzerWithFix.ReceivedCallback); Assert.True(analyzerWithoutFix.ReceivedCallback); @@ -178,7 +178,7 @@ public async Task TestGetFixesAsyncForDocumentDiagnosticAnalyzerAsync() // Verify both analyzers are executed when GetFixesAsync is invoked with 'CodeActionRequestPriority.Normal'. _ = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), - priorityProvider: new DefaultCodeActionRequestPriorityProvider(CodeActionRequestPriority.Default), + CodeActionRequestPriority.Default, cancellationToken: CancellationToken.None); Assert.True(documentDiagnosticAnalyzer.ReceivedCallback); } @@ -208,11 +208,11 @@ public async Task TestGetFixesAsyncForGeneratorDiagnosticAsync() Assert.False(codeFix.Called); var fixCollectionSet = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), - priorityProvider: new DefaultCodeActionRequestPriorityProvider(CodeActionRequestPriority.Default), + CodeActionRequestPriority.Default, cancellationToken: CancellationToken.None); Assert.True(codeFix.Called); var fixCollection = Assert.Single(fixCollectionSet); - Assert.Equal(MockFixer.Id, fixCollection.FirstDiagnostic.Id); + Assert.Equal(MockFixer.Id, fixCollection.Diagnostics.First().Id); var fix = Assert.Single(fixCollection.Fixes); Assert.Equal(fixTitle, fix.Action.Title); } @@ -303,7 +303,7 @@ private static async Task GetFirstDiagnosticWithFixWithExceptionValidationAsync( GetDocumentAndExtensionManager(workspace, out var document, out var extensionManager); var unused = await tuple.codeFixService.GetMostSevereFixAsync( - document, TextSpan.FromBounds(0, 0), new DefaultCodeActionRequestPriorityProvider(), CancellationToken.None); + document, TextSpan.FromBounds(0, 0), priority: null, CancellationToken.None); Assert.True(extensionManager.IsDisabled(codefix)); Assert.False(extensionManager.IsIgnored(codefix)); Assert.True(errorReported); @@ -1027,8 +1027,8 @@ void M() // We enable full solution analysis so the 'AnalyzeDocumentAsync' doesn't skip analysis based on whether the document is active/open. workspace.GlobalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, BackgroundAnalysisScope.FullSolution); - var analyzerService = workspace.Services.GetRequiredService(); - var diagnostics = await analyzerService.ForceAnalyzeProjectAsync(sourceDocument.Project, CancellationToken.None); + var analyzerService = (DiagnosticAnalyzerService)workspace.Services.GetRequiredService(); + var diagnostics = await analyzerService.ForceRunCodeAnalysisDiagnosticsAsync(sourceDocument.Project, CancellationToken.None); await VerifyCachedDiagnosticsAsync( sourceDocument, expectedCachedDiagnostic: diagnosticOnFixLineInPriorSnapshot, testSpan, diagnostics); @@ -1060,15 +1060,12 @@ await VerifyCachedDiagnosticsAsync( : root.DescendantNodes().OfType().First().Span; await analyzerService.GetDiagnosticsForIdsAsync( - sourceDocument.Project, [sourceDocument.Id], diagnosticIds: null, shouldIncludeAnalyzer: null, + sourceDocument.Project, [sourceDocument.Id], diagnosticIds: null, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); // await diagnosticIncrementalAnalyzer.GetTestAccessor().TextDocumentOpenAsync(sourceDocument); - var lowPriorityAnalyzerData = new SuggestedActionPriorityProvider.LowPriorityAnalyzersAndDiagnosticIds(); - var priorityProvider = new SuggestedActionPriorityProvider(CodeActionRequestPriority.Default, lowPriorityAnalyzerData); - var normalPriFixes = await tuple.codeFixService.GetFixesAsync(sourceDocument, testSpan, priorityProvider, CancellationToken.None); - priorityProvider = new SuggestedActionPriorityProvider(CodeActionRequestPriority.Low, lowPriorityAnalyzerData); - var lowPriFixes = await tuple.codeFixService.GetFixesAsync(sourceDocument, testSpan, priorityProvider, CancellationToken.None); + var normalPriFixes = await tuple.codeFixService.GetFixesAsync(sourceDocument, testSpan, CodeActionRequestPriority.Default, CancellationToken.None); + var lowPriFixes = await tuple.codeFixService.GetFixesAsync(sourceDocument, testSpan, CodeActionRequestPriority.Low, CancellationToken.None); if (expectedNoFixes) { @@ -1077,21 +1074,24 @@ await analyzerService.GetDiagnosticsForIdsAsync( return; } + var deprioritizedAnalyzers = await analyzerService.GetTestAccessor().GetDeprioritizedAnalyzersAsync(sourceDocument.Project); + var deprioritizedIds = await analyzerService.GetTestAccessor().GetDeprioritizedDiagnosticIdsAsync(sourceDocument.Project); + CodeFixCollection expectedFixCollection; if (expectDeprioritization) { Assert.Empty(normalPriFixes); expectedFixCollection = Assert.Single(lowPriFixes); - var lowPriorityAnalyzer = Assert.Single(lowPriorityAnalyzerData.Analyzers); + var lowPriorityAnalyzer = Assert.Single(deprioritizedAnalyzers); Assert.Same(analyzer, lowPriorityAnalyzer); - Assert.Equal(analyzer.SupportedDiagnostics.Select(d => d.Id), lowPriorityAnalyzerData.SupportedDiagnosticIds); + AssertEx.SetEqual(analyzer.SupportedDiagnostics.Select(d => d.Id), deprioritizedIds); } else { expectedFixCollection = Assert.Single(normalPriFixes); Assert.Empty(lowPriFixes); - Assert.Empty(lowPriorityAnalyzerData.Analyzers); - Assert.Empty(lowPriorityAnalyzerData.SupportedDiagnosticIds); + Assert.Empty(deprioritizedAnalyzers); + Assert.Empty(deprioritizedIds); } var fix = expectedFixCollection.Fixes.Single(); diff --git a/src/roslyn/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/roslyn/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index ba1ce46177c..1d4d091db66 100644 --- a/src/roslyn/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/roslyn/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -16,7 +16,6 @@ using Microsoft.CodeAnalysis.Editor.UnitTests.Extensions; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Remote.Diagnostics; using Microsoft.CodeAnalysis.Remote.Testing; using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -40,7 +39,7 @@ public sealed class DiagnosticAnalyzerServiceTests private static readonly TestComposition s_editorFeaturesCompositionWithMockDiagnosticUpdateSourceRegistrationService = EditorTestCompositions.EditorFeatures; private static AdhocWorkspace CreateWorkspace(Type[] additionalParts = null) - => new AdhocWorkspace(s_featuresCompositionWithMockDiagnosticUpdateSourceRegistrationService.AddParts(additionalParts).GetHostServices()); + => new(s_featuresCompositionWithMockDiagnosticUpdateSourceRegistrationService.AddParts(additionalParts).GetHostServices()); private static IGlobalOptionService GetGlobalOptions(Workspace workspace) => workspace.Services.SolutionServices.ExportProvider.GetExportedValue(); @@ -66,7 +65,7 @@ public async Task TestHasSuccessfullyLoadedBeingFalse() var service = workspace.Services.GetRequiredService(); var diagnostics = await service.GetDiagnosticsForIdsAsync( - workspace.CurrentSolution.Projects.Single(), documentIds: default, diagnosticIds: null, shouldIncludeAnalyzer: null, + workspace.CurrentSolution.Projects.Single(), documentIds: default, diagnosticIds: null, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); Assert.NotEmpty(diagnostics); } @@ -184,7 +183,7 @@ public async Task TestDisabledByDefaultAnalyzerEnabledWithEditorConfig(bool enab // open document workspace.OpenDocument(document.Id); - var diagnostics = await service.ForceAnalyzeProjectAsync(document.Project, CancellationToken.None); + var diagnostics = await service.ForceRunCodeAnalysisDiagnosticsAsync(document.Project, CancellationToken.None); foreach (var diagnostic in diagnostics) { @@ -218,7 +217,7 @@ private static async Task TestAnalyzerAsync( var syntax = false; var semantic = false; - var diagnostics = await service.ForceAnalyzeProjectAsync(document.Project, CancellationToken.None); + var diagnostics = await service.ForceRunCodeAnalysisDiagnosticsAsync(document.Project, CancellationToken.None); (syntax, semantic) = resultSetter(syntax, semantic, diagnostics); @@ -258,7 +257,7 @@ public async Task TestHostAnalyzerErrorNotLeaking() var service = workspace.Services.GetRequiredService(); - var diagnostics = await service.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostics = await service.ForceRunCodeAnalysisDiagnosticsAsync(project, CancellationToken.None); Assert.NotEmpty(diagnostics); } @@ -341,7 +340,7 @@ private static async Task TestFullSolutionAnalysisForProjectAsync(AdhocWorkspace { var service = workspace.Services.GetRequiredService(); - var diagnostics = await service.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostics = await service.ForceRunCodeAnalysisDiagnosticsAsync(project, CancellationToken.None); if (expectAnalyzerExecuted) { @@ -390,7 +389,7 @@ internal async Task TestAdditionalFileAnalyzer(bool registerFromInitialize, bool workspace.OpenAdditionalDocument(firstAdditionalDocument.Id); - var diagnostics = await service.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostics = await service.ForceRunCodeAnalysisDiagnosticsAsync(project, CancellationToken.None); var expectedCount = testMultiple ? 4 : 1; @@ -471,7 +470,7 @@ internal async Task TestDiagnosticSuppressor(bool includeAnalyzer, bool includeS throw ExceptionUtilities.UnexpectedValue(analysisScope); } - var diagnostics = await service.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostics = await service.ForceRunCodeAnalysisDiagnosticsAsync(project, CancellationToken.None); var diagnostic = diagnostics.SingleOrDefault(); if (includeAnalyzer) @@ -593,7 +592,7 @@ void M() break; } - var diagnostics = await service.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostics = await service.ForceRunCodeAnalysisDiagnosticsAsync(project, CancellationToken.None); diagnostics = [.. diagnostics .Where(d => d.Id == IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId) @@ -627,31 +626,20 @@ internal async Task TestOnlyRequiredAnalyzerExecutedDuringDiagnosticComputation( // Verify that requesting analyzer diagnostics for analyzer1 does not lead to invoking analyzer2. var analyzer1 = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, DiagnosticSeverity.Warning, throwOnAllNamedTypes: false); - var analyzer1Id = analyzer1.GetAnalyzerId(); var analyzer2 = new NamedTypeAnalyzer(); - var analyzerIdsToRequestDiagnostics = ImmutableArray.Create(analyzer1Id); var analyzerReference = new AnalyzerImageReference([analyzer1, analyzer2]); workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences([analyzerReference])); var project = workspace.CurrentSolution.Projects.Single(); var document = documentAnalysis ? project.Documents.Single() : null; - var diagnosticsMapResults = await DiagnosticComputer.GetDiagnosticsAsync( - document, project, Checksum.Null, span: null, projectAnalyzerIds: [], analyzerIdsToRequestDiagnostics, - AnalysisKind.Semantic, new DiagnosticAnalyzerInfoCache(), workspace.Services, - logPerformanceInfo: false, getTelemetryInfo: false, - cancellationToken: CancellationToken.None); + + var service = project.Solution.Services.GetRequiredService(); + var diagnosticsMapResults = await service.GetDiagnosticsForIdsAsync( + project, documentIds: default, [analyzer1.Descriptor.Id], AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); Assert.False(analyzer2.ReceivedSymbolCallback); - Assert.Equal(1, diagnosticsMapResults.Diagnostics.Length); - var (actualAnalyzerId, diagnosticMap) = diagnosticsMapResults.Diagnostics.Single(); - Assert.Equal(analyzer1Id, actualAnalyzerId); - Assert.Equal(1, diagnosticMap.Semantic.Length); - var semanticDiagnostics = diagnosticMap.Semantic.Single().Item2; - var diagnostic = Assert.Single(semanticDiagnostics); + Assert.Equal(1, diagnosticsMapResults.Length); + var diagnostic = diagnosticsMapResults.Single(); Assert.Equal(analyzer1.Descriptor.Id, diagnostic.Id); - - Assert.Empty(diagnosticMap.Syntax); - Assert.Empty(diagnosticMap.NonLocal); - Assert.Empty(diagnosticMap.Other); } [Theory, CombinatorialData] @@ -661,7 +649,6 @@ internal async Task TestAnalysisWithAnalyzerInBothProjectAndHost_SameAnalyzerIns using var workspace = TestWorkspace.CreateCSharp("class A { }"); var analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, DiagnosticSeverity.Warning, throwOnAllNamedTypes: false); - var analyzerId = analyzer.GetAnalyzerId(); var analyzerReference = new AnalyzerImageReference([analyzer]); workspace.TryApplyChanges( @@ -670,16 +657,12 @@ internal async Task TestAnalysisWithAnalyzerInBothProjectAndHost_SameAnalyzerIns var project = workspace.CurrentSolution.Projects.Single(); var document = documentAnalysis ? project.Documents.Single() : null; - var diagnosticsMapResults = await DiagnosticComputer.GetDiagnosticsAsync( - document, project, Checksum.Null, span: null, projectAnalyzerIds: [analyzerId], [analyzerId], - AnalysisKind.Semantic, new DiagnosticAnalyzerInfoCache(), workspace.Services, - logPerformanceInfo: false, getTelemetryInfo: false, - cancellationToken: CancellationToken.None); + var service = project.Solution.Services.GetRequiredService(); + var diagnosticsMapResults = await service.GetDiagnosticsForIdsAsync( + project, documentIds: default, [analyzer.Descriptor.Id], AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); // In this case, since the analyzer identity is identical, we ran it once - var analyzerResults = diagnosticsMapResults.Diagnostics.Single(); - Assert.Equal(analyzerId, analyzerResults.Item1); - Assert.Equal(1, analyzerResults.Item2.Semantic.Length); + Assert.Single(diagnosticsMapResults); } [Theory, CombinatorialData] @@ -689,16 +672,16 @@ internal async Task TestAnalysisWithAnalyzerInBothProjectAndHost_DifferentAnalyz using var workspace = TestWorkspace.CreateCSharp("class A { }"); var analyzerProject = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, DiagnosticSeverity.Warning, throwOnAllNamedTypes: false); - var analyzerProjectId = analyzerProject.GetAnalyzerId(); var analyzerProjectReference = new AnalyzerImageReference([analyzerProject]); var analyzerHost = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, DiagnosticSeverity.Warning, throwOnAllNamedTypes: false); - var analyzerHostId = analyzerHost.GetAnalyzerId(); var analyzerHostReference = new AnalyzerImageReference([analyzerHost]); + SerializerService.TestAccessor.AddAnalyzerImageReferences([analyzerProjectReference, analyzerHostReference]); + // AnalyzerImageReference will create a separate AnalyzerImageReference.Id for each instance created, so these will be different. Assert.NotEqual(analyzerProjectReference.Id, analyzerHostReference.Id); - Assert.Equal(analyzerProjectId, analyzerHostId); + Assert.Equal(analyzerProject.GetAnalyzerId(), analyzerHost.GetAnalyzerId()); workspace.TryApplyChanges( workspace.CurrentSolution.WithAnalyzerReferences([analyzerHostReference]) @@ -706,16 +689,14 @@ internal async Task TestAnalysisWithAnalyzerInBothProjectAndHost_DifferentAnalyz var project = workspace.CurrentSolution.Projects.Single(); var document = documentAnalysis ? project.Documents.Single() : null; - var diagnosticsMapResults = await DiagnosticComputer.GetDiagnosticsAsync( - document, project, Checksum.Null, span: null, projectAnalyzerIds: [analyzerProjectId], [analyzerHostId], - AnalysisKind.Semantic, new DiagnosticAnalyzerInfoCache(), workspace.Services, - logPerformanceInfo: false, getTelemetryInfo: false, - cancellationToken: CancellationToken.None); + var service = project.Solution.Services.GetRequiredService(); + var diagnosticsMapResults = await service.GetDiagnosticsForIdsAsync( + project, documentIds: default, [analyzerProject.Descriptor.Id, analyzerHost.Descriptor.Id], + AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); // In this case, since the analyzer reference identity is identical, we ran it once - var analyzerResults = diagnosticsMapResults.Diagnostics.Single(); - Assert.Equal(analyzerHostId, analyzerResults.Item1); - Assert.Equal(1, analyzerResults.Item2.Semantic.Length); + var analyzerResults = diagnosticsMapResults.Single(); + Assert.Single(diagnosticsMapResults); } [Theory, CombinatorialData] @@ -725,15 +706,13 @@ internal async Task TestAnalysisWithAnalyzerInBothProjectAndHost_DifferentAnalyz using var workspace = TestWorkspace.CreateCSharp("class A { }"); var analyzerProject = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, DiagnosticSeverity.Warning, throwOnAllNamedTypes: false); - var analyzerProjectId = analyzerProject.GetAnalyzerId(); var analyzerProjectReference = CreateAnalyzerReferenceWithSameId(analyzerProject); var analyzerHost = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, DiagnosticSeverity.Warning, throwOnAllNamedTypes: false); - var analyzerHostId = analyzerHost.GetAnalyzerId(); var analyzerHostReference = CreateAnalyzerReferenceWithSameId(analyzerHost); Assert.Equal(analyzerProjectReference.Id, analyzerHostReference.Id); - Assert.Equal(analyzerProjectId, analyzerHostId); + Assert.Equal(analyzerProject.GetAnalyzerId(), analyzerHost.GetAnalyzerId()); workspace.TryApplyChanges( workspace.CurrentSolution.WithAnalyzerReferences(AddExtraReferenceIfNeeded(analyzerHostReference, includeExtraHostReference)) @@ -741,16 +720,13 @@ internal async Task TestAnalysisWithAnalyzerInBothProjectAndHost_DifferentAnalyz var project = workspace.CurrentSolution.Projects.Single(); var document = documentAnalysis ? project.Documents.Single() : null; - var diagnosticsMapResults = await DiagnosticComputer.GetDiagnosticsAsync( - document, project, Checksum.Null, span: null, projectAnalyzerIds: [analyzerProjectId], [analyzerHostId], - AnalysisKind.Semantic, new DiagnosticAnalyzerInfoCache(), workspace.Services, - logPerformanceInfo: false, getTelemetryInfo: false, - cancellationToken: CancellationToken.None); + var service = project.Solution.Services.GetRequiredService(); + var diagnosticsMapResults = await service.GetDiagnosticsForIdsAsync( + project, documentIds: default, [analyzerProject.Descriptor.Id, analyzerHost.Descriptor.Id], + AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); - // In this case, the analyzers are ran twice. This appears to be a bug in SkippedHostAnalyzersInfo.Create, because it calls - // HostDiagnosticAnalyzers.CreateProjectDiagnosticAnalyzersPerReference which already filters out references, it doesn't return any - // references to skip. - Assert.Equal(2, diagnosticsMapResults.Diagnostics.Length); + // We should only get one diagnostic as the two analyzers have the same ID and will be deduped. + Assert.Equal(1, diagnosticsMapResults.Length); static AnalyzerReference CreateAnalyzerReferenceWithSameId(DiagnosticAnalyzer analyzer) { @@ -762,11 +738,13 @@ static AnalyzerReference CreateAnalyzerReferenceWithSameId(DiagnosticAnalyzer an return new TestAnalyzerReferenceByLanguage(map); } - static ImmutableArray AddExtraReferenceIfNeeded(AnalyzerReference mainReference, bool addExtraReference) + ImmutableArray AddExtraReferenceIfNeeded(AnalyzerReference mainReference, bool addExtraReference) { if (addExtraReference) { - return [mainReference, new AnalyzerImageReference([new FieldAnalyzer("FA1234", syntaxTreeAction: false)])]; + var imageReference = new AnalyzerImageReference([new FieldAnalyzer("FA1234", syntaxTreeAction: false)]); + SerializerService.TestAccessor.AddAnalyzerImageReference(imageReference); + return [mainReference, imageReference]; } else { @@ -785,8 +763,6 @@ internal async Task TestAnalysisWithAnalyzerInBothProjectAndHost_SameAnalyzerIns var analyzerProjectReference = CreateAnalyzerReferenceWithSameId(analyzer); var analyzerHostReference = CreateAnalyzerReferenceWithSameId(analyzer); - var analyzerId = analyzer.GetAnalyzerId(); - Assert.Equal(analyzerProjectReference.Id, analyzerHostReference.Id); workspace.TryApplyChanges( @@ -795,13 +771,12 @@ internal async Task TestAnalysisWithAnalyzerInBothProjectAndHost_SameAnalyzerIns var project = workspace.CurrentSolution.Projects.Single(); var document = documentAnalysis ? project.Documents.Single() : null; - var diagnosticsMapResults = await DiagnosticComputer.GetDiagnosticsAsync( - document, project, Checksum.Null, span: null, projectAnalyzerIds: [analyzerId], [analyzerId], - AnalysisKind.Semantic, new DiagnosticAnalyzerInfoCache(), workspace.Services, - logPerformanceInfo: false, getTelemetryInfo: false, - cancellationToken: CancellationToken.None); + var service = project.Solution.Services.GetRequiredService(); + var diagnosticsMapResults = await service.GetDiagnosticsForIdsAsync( + project, documentIds: default, [analyzer.Descriptor.Id], + AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); - Assert.Equal(1, diagnosticsMapResults.Diagnostics.Length); + Assert.Single(diagnosticsMapResults); static AnalyzerReference CreateAnalyzerReferenceWithSameId(DiagnosticAnalyzer analyzer) { @@ -832,9 +807,8 @@ void M() project = project.AddAdditionalDocument("additional.txt", @"This is an additional file!").Project; var analyzer = new FilterSpanTestAnalyzer(kind); - var analyzerId = analyzer.GetAnalyzerId(); - var analyzerIdsToRequestDiagnostics = ImmutableArray.Create(analyzerId); var analyzerReference = new AnalyzerImageReference([analyzer]); + SerializerService.TestAccessor.AddAnalyzerImageReference(analyzerReference); project = project.AddAnalyzerReference(analyzerReference); workspace.TryApplyChanges(project.Solution); @@ -858,14 +832,12 @@ void M() async Task VerifyCallbackSpanAsync(TextSpan? filterSpan) { var analysisKind = kind is FilterSpanTestAnalyzer.AnalysisKind.SyntaxTree or FilterSpanTestAnalyzer.AnalysisKind.AdditionalFile - ? AnalysisKind.Syntax - : AnalysisKind.Semantic; + ? DiagnosticKind.AnalyzerSyntax + : DiagnosticKind.AnalyzerSemantic; var documentToAnalyze = kind == FilterSpanTestAnalyzer.AnalysisKind.AdditionalFile ? additionalDocument : document; - _ = await DiagnosticComputer.GetDiagnosticsAsync( - documentToAnalyze, project, Checksum.Null, filterSpan, analyzerIdsToRequestDiagnostics, hostAnalyzerIds: [], - analysisKind, new DiagnosticAnalyzerInfoCache(), workspace.Services, - logPerformanceInfo: false, getTelemetryInfo: false, - CancellationToken.None); + var service = project.Solution.Services.GetRequiredService(); + _ = await service.GetDiagnosticsForSpanAsync( + documentToAnalyze, filterSpan, analysisKind, CancellationToken.None); Assert.Equal(filterSpan, analyzer.CallbackFilterSpan); if (kind == FilterSpanTestAnalyzer.AnalysisKind.AdditionalFile) { @@ -909,18 +881,17 @@ void M() var project = workspace.CurrentSolution.Projects.Single(); var document = project.Documents.Single(); - var diagnosticAnalyzerInfoCache = new DiagnosticAnalyzerInfoCache(); var kind = actionKind == AnalyzerRegisterActionKind.SyntaxTree ? AnalysisKind.Syntax : AnalysisKind.Semantic; - var analyzerIds = ImmutableArray.Create(analyzer.GetAnalyzerId()); // First invoke analysis with cancellation token, and verify canceled compilation and no reported diagnostics. Assert.Empty(analyzer.CanceledCompilations); + var service = project.Solution.Services.GetRequiredService(); try { - _ = await DiagnosticComputer.GetDiagnosticsAsync(document, project, Checksum.Null, span: null, - projectAnalyzerIds: [], analyzerIds, kind, diagnosticAnalyzerInfoCache, workspace.Services, - logPerformanceInfo: false, getTelemetryInfo: false, cancellationToken: analyzer.CancellationToken); + _ = await service.GetDiagnosticsForIdsAsync( + project, [document.Id], diagnosticIds: null, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, + analyzer.CancellationToken); throw ExceptionUtilities.Unreachable(); } @@ -928,15 +899,10 @@ void M() { } - Assert.Single(analyzer.CanceledCompilations); - // Then invoke analysis without cancellation token, and verify non-cancelled diagnostic. - var diagnosticsMap = await DiagnosticComputer.GetDiagnosticsAsync(document, project, Checksum.Null, span: null, - projectAnalyzerIds: [], analyzerIds, kind, diagnosticAnalyzerInfoCache, workspace.Services, - logPerformanceInfo: false, getTelemetryInfo: false, cancellationToken: CancellationToken.None); - var builder = diagnosticsMap.Diagnostics.Single().diagnosticMap; - var diagnostic = kind == AnalysisKind.Syntax ? builder.Syntax.Single().Item2.Single() : builder.Semantic.Single().Item2.Single(); - Assert.Equal(CancellationTestAnalyzer.DiagnosticId, diagnostic.Id); + var diagnosticsMap = await service.GetDiagnosticsForIdsAsync( + project, [document.Id], diagnosticIds: null, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); + Assert.Equal(CancellationTestAnalyzer.DiagnosticId, diagnosticsMap.Single().Id); } [Theory, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1909806")] @@ -964,7 +930,7 @@ internal async Task TestGeneratorProducedDiagnostics(bool fullSolutionAnalysis, var service = workspace.Services.GetRequiredService(); - var diagnostics = await service.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostics = await service.ForceRunCodeAnalysisDiagnosticsAsync(project, CancellationToken.None); Assert.NotEmpty(diagnostics); } @@ -1003,9 +969,9 @@ private static (bool, bool) CompilerAnalyzerResultSetter(bool syntax, bool seman private sealed class Analyzer : DiagnosticAnalyzer { - internal static readonly DiagnosticDescriptor s_syntaxRule = new DiagnosticDescriptor("syntax", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true); - internal static readonly DiagnosticDescriptor s_semanticRule = new DiagnosticDescriptor("semantic", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true); - internal static readonly DiagnosticDescriptor s_compilationRule = new DiagnosticDescriptor("compilation", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true); + internal static readonly DiagnosticDescriptor s_syntaxRule = new("syntax", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true); + internal static readonly DiagnosticDescriptor s_semanticRule = new("semantic", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true); + internal static readonly DiagnosticDescriptor s_compilationRule = new("compilation", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics => [s_syntaxRule, s_semanticRule, s_compilationRule]; @@ -1019,9 +985,9 @@ public override void Initialize(AnalysisContext context) private sealed class DisabledByDefaultAnalyzer : DiagnosticAnalyzer { - internal static readonly DiagnosticDescriptor s_syntaxRule = new DiagnosticDescriptor("syntax", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: false); - internal static readonly DiagnosticDescriptor s_semanticRule = new DiagnosticDescriptor("semantic", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: false); - internal static readonly DiagnosticDescriptor s_compilationRule = new DiagnosticDescriptor("compilation", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: false); + internal static readonly DiagnosticDescriptor s_syntaxRule = new("syntax", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: false); + internal static readonly DiagnosticDescriptor s_semanticRule = new("semantic", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: false); + internal static readonly DiagnosticDescriptor s_compilationRule = new("compilation", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: false); public override ImmutableArray SupportedDiagnostics => [s_syntaxRule, s_semanticRule, s_compilationRule]; @@ -1044,7 +1010,7 @@ protected PriorityTestDocumentDiagnosticAnalyzer(int priority) private sealed class LeakDocumentAnalyzer : DocumentDiagnosticAnalyzer { - internal static readonly DiagnosticDescriptor s_syntaxRule = new DiagnosticDescriptor("leak", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true); + internal static readonly DiagnosticDescriptor s_syntaxRule = new("leak", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics => [s_syntaxRule]; diff --git a/src/roslyn/src/EditorFeatures/Test/DocCommentFormatting/DocCommentFormattingTests.cs b/src/roslyn/src/EditorFeatures/Test/DocCommentFormatting/DocCommentFormattingTests.cs index 446a08c4405..c53ddb003aa 100644 --- a/src/roslyn/src/EditorFeatures/Test/DocCommentFormatting/DocCommentFormattingTests.cs +++ b/src/roslyn/src/EditorFeatures/Test/DocCommentFormatting/DocCommentFormattingTests.cs @@ -15,8 +15,8 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.DocCommentFormatting; [Trait(Traits.Feature, Traits.Features.DocCommentFormatting)] public sealed class DocCommentFormattingTests { - private readonly CSharpDocumentationCommentFormattingService _csharpService = new CSharpDocumentationCommentFormattingService(); - private readonly VisualBasicDocumentationCommentFormattingService _vbService = new VisualBasicDocumentationCommentFormattingService(); + private readonly CSharpDocumentationCommentFormattingService _csharpService = new(); + private readonly VisualBasicDocumentationCommentFormattingService _vbService = new(); private void TestFormat(string xmlFragment, string expectedCSharp, string expectedVB) { diff --git a/src/roslyn/src/EditorFeatures/Test/EditAndContinue/EditAndContinueLanguageServiceTests.cs b/src/roslyn/src/EditorFeatures/Test/EditAndContinue/EditAndContinueLanguageServiceTests.cs index b711c7f3475..650609b224c 100644 --- a/src/roslyn/src/EditorFeatures/Test/EditAndContinue/EditAndContinueLanguageServiceTests.cs +++ b/src/roslyn/src/EditorFeatures/Test/EditAndContinue/EditAndContinueLanguageServiceTests.cs @@ -319,6 +319,12 @@ await localWorkspace.ChangeSolutionAsync(localWorkspace.CurrentSolution Assert.True(sessionState.IsSessionActive); +#pragma warning disable CS0612 // Type or member is obsolete + // validate that obsolete overload does not throw for empty array: + _ = await localService.GetUpdatesAsync(runningProjects: ImmutableArray.Empty, CancellationToken.None); + Assert.Equal(++observedDiagnosticVersion, diagnosticRefresher.GlobalStateVersion); +#pragma warning restore + if (commitChanges) { // CommitUpdatesAsync diff --git a/src/roslyn/src/EditorFeatures/Test/MetadataAsSource/DocCommentFormatterTests.cs b/src/roslyn/src/EditorFeatures/Test/MetadataAsSource/DocCommentFormatterTests.cs index 54c1bece379..cd34b7ac903 100644 --- a/src/roslyn/src/EditorFeatures/Test/MetadataAsSource/DocCommentFormatterTests.cs +++ b/src/roslyn/src/EditorFeatures/Test/MetadataAsSource/DocCommentFormatterTests.cs @@ -17,8 +17,8 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.MetadataAsSource; [Trait(Traits.Feature, Traits.Features.MetadataAsSource)] public sealed class DocCommentFormatterTests { - private readonly CSharpDocumentationCommentFormattingService _csharpService = new CSharpDocumentationCommentFormattingService(); - private readonly VisualBasicDocumentationCommentFormattingService _vbService = new VisualBasicDocumentationCommentFormattingService(); + private readonly CSharpDocumentationCommentFormattingService _csharpService = new(); + private readonly VisualBasicDocumentationCommentFormattingService _vbService = new(); private void TestFormat(string docCommentXmlFragment, string expected) => TestFormat(docCommentXmlFragment, expected, expected); diff --git a/src/roslyn/src/EditorFeatures/Test/RenameTracking/RenameTrackingTestState.cs b/src/roslyn/src/EditorFeatures/Test/RenameTracking/RenameTrackingTestState.cs index 9ac0a85996e..ed06134b557 100644 --- a/src/roslyn/src/EditorFeatures/Test/RenameTracking/RenameTrackingTestState.cs +++ b/src/roslyn/src/EditorFeatures/Test/RenameTracking/RenameTrackingTestState.cs @@ -48,7 +48,7 @@ internal sealed class RenameTrackingTestState : IDisposable public MockRefactorNotifyService RefactorNotifyService { get; } private readonly RenameTrackingCodeRefactoringProvider _codeRefactoringProvider; - private readonly RenameTrackingCancellationCommandHandler _commandHandler = new RenameTrackingCancellationCommandHandler(); + private readonly RenameTrackingCancellationCommandHandler _commandHandler = new(); public static RenameTrackingTestState Create( string markup, diff --git a/src/roslyn/src/EditorFeatures/Test/Utilities/BloomFilterTests.cs b/src/roslyn/src/EditorFeatures/Test/Utilities/BloomFilterTests.cs index 222eb6de371..899e09069ad 100644 --- a/src/roslyn/src/EditorFeatures/Test/Utilities/BloomFilterTests.cs +++ b/src/roslyn/src/EditorFeatures/Test/Utilities/BloomFilterTests.cs @@ -208,15 +208,15 @@ public void TestCacheCorrectness(bool isCaseSensitive, bool reverse) var comparer = isCaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase; var allHashSets = new List> { - new HashSet(GenerateStrings(1_000), comparer), - new HashSet(GenerateStrings(1_000).Where((s, i) => i % 1 == 0), comparer), - new HashSet(GenerateStrings(1_000).Where((s, i) => i % 1 == 1), comparer), - new HashSet(GenerateStrings(10_000), comparer), - new HashSet(GenerateStrings(10_000).Where((s, i) => i % 1 == 0), comparer), - new HashSet(GenerateStrings(10_000).Where((s, i) => i % 1 == 1), comparer), - new HashSet(GenerateStrings(100_000), comparer), - new HashSet(GenerateStrings(100_000).Where((s, i) => i % 1 == 0), comparer), - new HashSet(GenerateStrings(100_000).Where((s, i) => i % 1 == 1), comparer), + new(GenerateStrings(1_000), comparer), + new(GenerateStrings(1_000).Where((s, i) => i % 1 == 0), comparer), + new(GenerateStrings(1_000).Where((s, i) => i % 1 == 1), comparer), + new(GenerateStrings(10_000), comparer), + new(GenerateStrings(10_000).Where((s, i) => i % 1 == 0), comparer), + new(GenerateStrings(10_000).Where((s, i) => i % 1 == 1), comparer), + new(GenerateStrings(100_000), comparer), + new(GenerateStrings(100_000).Where((s, i) => i % 1 == 0), comparer), + new(GenerateStrings(100_000).Where((s, i) => i % 1 == 1), comparer), }; // Try the patterns where we're searching smaller filters then larger ones. Then the pattern of larger ones then smaller ones. diff --git a/src/roslyn/src/EditorFeatures/Test/Utilities/SymbolEquivalenceComparerTests.cs b/src/roslyn/src/EditorFeatures/Test/Utilities/SymbolEquivalenceComparerTests.cs index b4c444072b0..f6621c4c2d5 100644 --- a/src/roslyn/src/EditorFeatures/Test/Utilities/SymbolEquivalenceComparerTests.cs +++ b/src/roslyn/src/EditorFeatures/Test/Utilities/SymbolEquivalenceComparerTests.cs @@ -21,7 +21,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities; [UseExportProvider] public sealed class SymbolEquivalenceComparerTests { - public static readonly CS.CSharpCompilationOptions CSharpDllOptions = new CS.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); + public static readonly CS.CSharpCompilationOptions CSharpDllOptions = new(OutputKind.DynamicallyLinkedLibrary); public static readonly CS.CSharpCompilationOptions CSharpSignedDllOptions = new CS.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary). WithCryptoKeyFile(SigningTestHelpers.KeyPairFile). WithStrongNameProvider(DefaultDesktopStrongNameProvider); diff --git a/src/roslyn/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb b/src/roslyn/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb index 8e079af3012..39700b106d5 100644 --- a/src/roslyn/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb @@ -293,7 +293,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.CodeFixes.UnitTests ' Make sure we don't crash Dim unused = Await codefixService.GetMostSevereFixAsync( - document, Text.TextSpan.FromBounds(0, 0), New DefaultCodeActionRequestPriorityProvider(), CancellationToken.None) + document, Text.TextSpan.FromBounds(0, 0), priority:=Nothing, CancellationToken.None) End Using End Function diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTest.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTests.vb similarity index 98% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTest.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTests.vb index 1eb3762d50b..c248f7132d6 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTest.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTests.vb @@ -18,7 +18,7 @@ Imports Xunit.Abstractions Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics <[UseExportProvider]> - Partial Public MustInherit Class AbstractCrossLanguageUserDiagnosticTest + Partial Public MustInherit Class AbstractCrossLanguageUserDiagnosticTests Private ReadOnly _outputHelper As ITestOutputHelper Protected Sub New(Optional outputHelper As ITestOutputHelper = Nothing) @@ -165,12 +165,12 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics For Each diagnostic In diagnostics Dim fixes = New List(Of CodeFix) - Dim context = New CodeFixContext(document, diagnostic, Sub(a, d) fixes.Add(New CodeFix(document.Project, a, d)), CancellationToken.None) + Dim context = New CodeFixContext(document, diagnostic, Sub(a, d) fixes.Add(New CodeFix(a, d)), CancellationToken.None) providerAndFixer.Item2.RegisterCodeFixesAsync(context).Wait() If fixes.Any() Then result.Add(Tuple.Create(diagnostic, New CodeFixCollection( fixer, diagnostic.Location.SourceSpan, fixes.ToImmutableArrayOrEmpty(), - fixAllState:=Nothing, supportedScopes:=Nothing, firstDiagnostic:=Nothing))) + fixAllState:=Nothing, supportedScopes:=Nothing, ImmutableArray.Create(diagnostic)))) End If Next diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageUserDiagnosticTests.vb similarity index 99% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageUserDiagnosticTests.vb index f5483325495..ed46c706b39 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageUserDiagnosticTests.vb @@ -14,8 +14,8 @@ Imports Roslyn.Utilities Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.AddImport - Public Class AddImportCrossLanguageTests - Inherits AbstractCrossLanguageUserDiagnosticTest + Public Class AddImportCrossLanguageUserDiagnosticTests + Inherits AbstractCrossLanguageUserDiagnosticTests Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) Dim fixer As CodeFixProvider diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddMissingReference/AddMissingReferenceTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddMissingReference/AddMissingReferenceCrossLanguageUserDiagnosticTests.vb similarity index 98% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddMissingReference/AddMissingReferenceTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddMissingReference/AddMissingReferenceCrossLanguageUserDiagnosticTests.vb index 2d460882719..94320f90d76 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddMissingReference/AddMissingReferenceTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddMissingReference/AddMissingReferenceCrossLanguageUserDiagnosticTests.vb @@ -10,8 +10,8 @@ Imports Microsoft.CodeAnalysis.VisualBasic.AddMissingReference Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.AddMissingReference - Public Class AddMissingReferenceTests - Inherits AbstractCrossLanguageUserDiagnosticTest + Public Class AddMissingReferenceCrossLanguageUserDiagnosticTests + Inherits AbstractCrossLanguageUserDiagnosticTests Private Shared ReadOnly s_presentationCoreAssembly As Assembly Private Shared ReadOnly s_presentationFrameworkAssembly As Assembly diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AdditionalFileDiagnosticsTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AdditionalFileCrossLanguageUserDiagnosticTests.vb similarity index 97% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/AdditionalFileDiagnosticsTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/AdditionalFileCrossLanguageUserDiagnosticTests.vb index 5a48606469e..b52a1133016 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AdditionalFileDiagnosticsTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AdditionalFileCrossLanguageUserDiagnosticTests.vb @@ -11,8 +11,8 @@ Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.UnitTests Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.AdditionalFiles - Public Class AdditionalFileDiagnosticsTests - Inherits AbstractCrossLanguageUserDiagnosticTest + Public Class AdditionalFileCrossLanguageUserDiagnosticTests + Inherits AbstractCrossLanguageUserDiagnosticTests Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = EditorTestCompositions.EditorFeatures diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb index f0bfa017e8e..59b974aa904 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb @@ -266,7 +266,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests For Each project In workspace.CurrentSolution.Projects actualDiagnostics.AddRange(diagnosticProvider.GetDiagnosticsForIdsAsync( - project, documentIds:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentIds:=Nothing, diagnosticIds:=Nothing, AnalyzerFilter.All, includeLocalDocumentDiagnostics:=True, CancellationToken.None).Result) Next diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb index f74a88534c6..f84817340d8 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb @@ -535,7 +535,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Assert.Empty(diagnostics) diagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( - project, documentIds:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentIds:=Nothing, diagnosticIds:=Nothing, AnalyzerFilter.All, includeLocalDocumentDiagnostics:=True, CancellationToken.None) Dim diagnostic = diagnostics.First() Assert.True(diagnostic.Id = "AD0001") @@ -606,7 +606,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim document = project.Documents.Single() Dim diagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( - project, documentIds:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentIds:=Nothing, diagnosticIds:=Nothing, AnalyzerFilter.All, includeLocalDocumentDiagnostics:=True, CancellationToken.None) Assert.Equal(1, diagnostics.Length) Dim diagnostic = diagnostics.First() @@ -805,7 +805,7 @@ class AnonymousFunctions ' Verify compilation diagnostics are reported with correct location info when asked for project diagnostics. Dim projectDiagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( project, documentIds:=Nothing, - diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + diagnosticIds:=Nothing, AnalyzerFilter.All, includeLocalDocumentDiagnostics:=True, CancellationToken.None) Assert.Equal(2, projectDiagnostics.Length) @@ -955,7 +955,7 @@ class AnonymousFunctions ' Verify no duplicate analysis/diagnostics. Dim document = project.Documents.Single() Dim diagnostics = (Await diagnosticService.GetDiagnosticsForIdsAsync( - project, documentIds:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentIds:=Nothing, diagnosticIds:=Nothing, AnalyzerFilter.All, includeLocalDocumentDiagnostics:=True, CancellationToken.None)). Select(Function(d) d.Id = NamedTypeAnalyzer.DiagDescriptor.Id) @@ -1050,7 +1050,7 @@ class AnonymousFunctions ' Verify project diagnostics contains diagnostics reported on both partial definitions. Dim diagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( - project, documentIds:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentIds:=Nothing, diagnosticIds:=Nothing, AnalyzerFilter.All, includeLocalDocumentDiagnostics:=True, CancellationToken.None) Assert.Equal(2, diagnostics.Length) Dim file1HasDiag = False, file2HasDiag = False @@ -2142,7 +2142,7 @@ class MyClass Assert.Equal(analyzer.Descriptor.Id, descriptors.Single().Id) ' Get cached project diagnostics. - Dim diagnostics = Await diagnosticService.ForceAnalyzeProjectAsync( + Dim diagnostics = Await diagnosticService.ForceRunCodeAnalysisDiagnosticsAsync( project, CancellationToken.None) ' in v2, solution crawler never creates non-local hidden diagnostics. @@ -2152,7 +2152,7 @@ class MyClass ' Get diagnostics explicitly Dim hiddenDiagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( - project, documentIds:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentIds:=Nothing, diagnosticIds:=Nothing, AnalyzerFilter.All, includeLocalDocumentDiagnostics:=True, CancellationToken.None) Assert.Equal(1, hiddenDiagnostics.Length) Assert.Equal(analyzer.Descriptor.Id, hiddenDiagnostics.Single().Id) @@ -2239,7 +2239,7 @@ class C Assert.Equal(1, descriptorsMap.Count) Dim diagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( - project, documentIds:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentIds:=Nothing, diagnosticIds:=Nothing, AnalyzerFilter.All, includeLocalDocumentDiagnostics:=True, CancellationToken.None) Assert.Empty(diagnostics) End Using diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateEvent/GenerateEventCrossLanguageTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateEvent/GenerateEventCrossLanguageUserDiagnosticTests.vb similarity index 97% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateEvent/GenerateEventCrossLanguageTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateEvent/GenerateEventCrossLanguageUserDiagnosticTests.vb index 7d8d9f26651..fef8e8728c4 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateEvent/GenerateEventCrossLanguageTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateEvent/GenerateEventCrossLanguageUserDiagnosticTests.vb @@ -7,8 +7,8 @@ Imports Microsoft.CodeAnalysis.Diagnostics Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.GenerateEvent - Public Class GenerateEventCrossLanguageTests - Inherits AbstractCrossLanguageUserDiagnosticTest + Public Class GenerateEventCrossLanguageUserDiagnosticTests + Inherits AbstractCrossLanguageUserDiagnosticTests Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) Return (Nothing, New Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateEvent.GenerateEventCodeFixProvider()) diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateConstructorCrossLanguageTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateConstructorCrossLanguageUserDiagnosticTests.vb similarity index 94% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateConstructorCrossLanguageTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateConstructorCrossLanguageUserDiagnosticTests.vb index 2bc96ee25ad..a5abc57a173 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateConstructorCrossLanguageTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateConstructorCrossLanguageUserDiagnosticTests.vb @@ -6,8 +6,8 @@ Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.Diagnostics Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.GenerateConstructor - Partial Public Class GenerateConstructorCrossLanguageTests - Inherits AbstractCrossLanguageUserDiagnosticTest + Partial Public Class GenerateConstructorCrossLanguageUserDiagnosticTests + Inherits AbstractCrossLanguageUserDiagnosticTests Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) If language = LanguageNames.CSharp Then diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageUserDiagnosticTests.vb similarity index 99% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageUserDiagnosticTests.vb index 71cb4dbecc9..232c94a78bf 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageUserDiagnosticTests.vb @@ -8,8 +8,8 @@ Imports Xunit.Abstractions Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.GenerateMethod - Partial Public Class GenerateMethodCrossLanguageTests - Inherits AbstractCrossLanguageUserDiagnosticTest + Partial Public Class GenerateMethodCrossLanguageUserDiagnosticTests + Inherits AbstractCrossLanguageUserDiagnosticTests Public Sub New(outputHelper As ITestOutputHelper) MyBase.New(outputHelper) diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageUserDiagnosticTests.vb similarity index 95% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageUserDiagnosticTests.vb index a41d267ee7f..8c40df74dc2 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageUserDiagnosticTests.vb @@ -10,8 +10,8 @@ Imports Microsoft.CodeAnalysis.VisualBasic.GenerateVariable Imports Xunit.Abstractions Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.GenerateVariable - Partial Public Class GenerateVariableCrossLanguageTests - Inherits AbstractCrossLanguageUserDiagnosticTest + Partial Public Class GenerateVariableCrossLanguageUserDiagnosticTests + Inherits AbstractCrossLanguageUserDiagnosticTests Public Sub New(outputHelper As ITestOutputHelper) MyBase.New(outputHelper) diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/ImplementInterface/ImplementInterfaceCrossLanguageTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/ImplementInterface/ImplementInterfaceCrossLanguageUserDiagnosticTests.vb similarity index 98% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/ImplementInterface/ImplementInterfaceCrossLanguageTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/ImplementInterface/ImplementInterfaceCrossLanguageUserDiagnosticTests.vb index a7f32cacf22..662c6c2a94c 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/ImplementInterface/ImplementInterfaceCrossLanguageTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/ImplementInterface/ImplementInterfaceCrossLanguageUserDiagnosticTests.vb @@ -9,8 +9,8 @@ Imports Microsoft.CodeAnalysis.VisualBasic.ImplementInterface Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.ImplementInterface - Public Class ImplementInterfaceCrossLanguageTests - Inherits AbstractCrossLanguageUserDiagnosticTest + Public Class ImplementInterfaceCrossLanguageUserDiagnosticTests + Inherits AbstractCrossLanguageUserDiagnosticTests Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) If language = LanguageNames.CSharp Then diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/UseAutoProperty/UseAutoPropertyTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/UseAutoProperty/UseAutoPropertyCrossLanguageUserDiagnosticTests.vb similarity index 98% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/UseAutoProperty/UseAutoPropertyTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/UseAutoProperty/UseAutoPropertyCrossLanguageUserDiagnosticTests.vb index 9b30c564330..a844a063a80 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/UseAutoProperty/UseAutoPropertyTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/UseAutoProperty/UseAutoPropertyCrossLanguageUserDiagnosticTests.vb @@ -9,8 +9,8 @@ Imports Microsoft.CodeAnalysis.VisualBasic.UseAutoProperty Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.UseAutoProperty - Public Class UseAutoPropertyTests - Inherits AbstractCrossLanguageUserDiagnosticTest + Public Class UseAutoPropertyCrossLanguageUserDiagnosticTests + Inherits AbstractCrossLanguageUserDiagnosticTests Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) If language = LanguageNames.CSharp Then diff --git a/src/roslyn/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb b/src/roslyn/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb index 7e399b64ddb..73e755a53fb 100644 --- a/src/roslyn/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb @@ -112,7 +112,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences d.Name, d.AnnotatedSpans(key).ToList())).ToList() Dim valueUsageInfoField = key.Substring(ValueUsageInfoKey.Length) Dim actual = GetFileNamesAndSpans( - context.References.Where(Function(r) r.SymbolUsageInfo.ValueUsageInfoOpt?.ToString() = valueUsageInfoField).Select(Function(r) r.SourceSpan)) + context.References.SelectAsArray(Function(r) r.SymbolUsageInfo.ValueUsageInfoOpt?.ToString() = valueUsageInfoField, Function(r) r.SourceSpan)) Assert.Equal(expected, actual) Next diff --git a/src/roslyn/src/EditorFeatures/Test2/GoToBase/VisuaBasicGoToBaseTests.vb b/src/roslyn/src/EditorFeatures/Test2/GoToBase/VisualBasicGoToBaseTests.vb similarity index 100% rename from src/roslyn/src/EditorFeatures/Test2/GoToBase/VisuaBasicGoToBaseTests.vb rename to src/roslyn/src/EditorFeatures/Test2/GoToBase/VisualBasicGoToBaseTests.vb diff --git a/src/roslyn/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_AwaitCompletion.vb b/src/roslyn/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_Await.vb similarity index 100% rename from src/roslyn/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_AwaitCompletion.vb rename to src/roslyn/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_Await.vb diff --git a/src/roslyn/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests_Exclusivitiy.vb b/src/roslyn/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests_Exclusivity.vb similarity index 100% rename from src/roslyn/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests_Exclusivitiy.vb rename to src/roslyn/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests_Exclusivity.vb diff --git a/src/roslyn/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests_AwaitCompletion.vb b/src/roslyn/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests_Await.vb similarity index 100% rename from src/roslyn/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests_AwaitCompletion.vb rename to src/roslyn/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests_Await.vb diff --git a/src/roslyn/src/EditorFeatures/Test2/Simplification/ModuleNameSimplificationTests.vb b/src/roslyn/src/EditorFeatures/Test2/Simplification/ModuleNameSimplifierTest.vb similarity index 100% rename from src/roslyn/src/EditorFeatures/Test2/Simplification/ModuleNameSimplificationTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Simplification/ModuleNameSimplifierTest.vb diff --git a/src/roslyn/src/EditorFeatures/Test2/Simplification/BlockSimplificationTests.vb b/src/roslyn/src/EditorFeatures/Test2/Simplification/SimplificationTests.vb similarity index 100% rename from src/roslyn/src/EditorFeatures/Test2/Simplification/BlockSimplificationTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Simplification/SimplificationTests.vb diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Async/Checkpoint.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Async/Checkpoint.cs index e325bf82e54..7e3c9c9b1e6 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Async/Checkpoint.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/Async/Checkpoint.cs @@ -10,7 +10,7 @@ namespace Roslyn.Test.Utilities; public class Checkpoint { - private readonly TaskCompletionSource _tcs = new TaskCompletionSource(); + private readonly TaskCompletionSource _tcs = new(); public Task Task => _tcs.Task; diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/BlindAggregatorFactory.cs b/src/roslyn/src/EditorFeatures/TestUtilities/BlindAggregatorFactory.cs index e8eafdab087..603403f9cf7 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/BlindAggregatorFactory.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/BlindAggregatorFactory.cs @@ -119,7 +119,7 @@ public unsafe CoTaskMemPtr() // Singleton instance of the VTable allocated in native memory. Since it's static, the // underlying native memory will be freed when finalizers run at shutdown. - private static readonly CoTaskMemPtr s_instance = new CoTaskMemPtr(); + private static readonly CoTaskMemPtr s_instance = new(); public static IntPtr AddressOfVTable { get { return s_instance.VTablePtr; } } } @@ -128,13 +128,13 @@ public unsafe CoTaskMemPtr() private const int E_NOINTERFACE = unchecked((int)0x80004002); // 00000000-0000-0000-C000-000000000046 - private static readonly Guid s_IUnknownInterfaceGuid = new Guid(0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); + private static readonly Guid s_IUnknownInterfaceGuid = new(0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); // 00000003-0000-0000-C000-000000000046 - private static readonly Guid s_IMarshalInterfaceGuid = new Guid(0x00000003, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); + private static readonly Guid s_IMarshalInterfaceGuid = new(0x00000003, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); // CBD71F2C-6BC5-4932-B851-B93EB3151386 - private static readonly Guid s_IComWrapperGuid = new Guid("CBD71F2C-6BC5-4932-B851-B93EB3151386"); + private static readonly Guid s_IComWrapperGuid = new("CBD71F2C-6BC5-4932-B851-B93EB3151386"); private static unsafe int QueryInterface(BlindAggregator* pThis, [In] ref Guid riid, out IntPtr pvObject) { diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.Operators.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.Operators.cs index 883c7bcaca4..7e4e043aa85 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.Operators.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.Operators.cs @@ -15,7 +15,7 @@ public static class Operators { [DebuggerStepThrough] private static FormattedClassification New(string text) - => new FormattedClassification(text, ClassificationTypeNames.Operator); + => new(text, ClassificationTypeNames.Operator); public static FormattedClassification Ampersand { get; } = New("&"); public static FormattedClassification AmpersandAmpersand { get; } = New("&&"); diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.OverloadedOperators.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.OverloadedOperators.cs index 5858d317f80..870b12fb570 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.OverloadedOperators.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.OverloadedOperators.cs @@ -15,7 +15,7 @@ public static class OverloadedOperators { [DebuggerStepThrough] private static FormattedClassification New(string text) - => new FormattedClassification(text, ClassificationTypeNames.OperatorOverloaded); + => new(text, ClassificationTypeNames.OperatorOverloaded); public static FormattedClassification Ampersand { get; } = New("&"); public static FormattedClassification AmpersandEquals { get; } = New("&="); diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.Punctuation.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.Punctuation.cs index 9cb7bb34162..5b2b4dc4691 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.Punctuation.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.Punctuation.cs @@ -15,7 +15,7 @@ public static class Punctuation { [DebuggerStepThrough] private static FormattedClassification New(string text) - => new FormattedClassification(text, ClassificationTypeNames.Punctuation); + => new(text, ClassificationTypeNames.Punctuation); public static FormattedClassification OpenCurly { get; } = New("{"); public static FormattedClassification CloseCurly { get; } = New("}"); diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs index 2c8fb423ea4..081f0c60953 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Classification; public static partial class FormattedClassifications { private static FormattedClassification New(string text, string typeName) - => new FormattedClassification(text, typeName); + => new(text, typeName); [DebuggerStepThrough] public static FormattedClassification Struct(string text) diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Extensions/IDiagnosticServiceExtensions.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Extensions/IDiagnosticServiceExtensions.cs deleted file mode 100644 index e8bc36f4040..00000000000 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Extensions/IDiagnosticServiceExtensions.cs +++ /dev/null @@ -1,20 +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.Immutable; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace Microsoft.CodeAnalysis.Editor.UnitTests.Extensions; - -internal static class IDiagnosticServiceExtensions -{ - public static Task> ForceAnalyzeProjectAsync( - this IDiagnosticAnalyzerService service, Project project, CancellationToken cancellationToken) - { - return CodeAnalysisDiagnosticAnalyzerServiceHelpers.ForceCodeAnalysisDiagnosticsAsync( - service, project, new(), cancellationToken); - } -} diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/NavigateTo/AbstractNavigateToTests.cs b/src/roslyn/src/EditorFeatures/TestUtilities/NavigateTo/AbstractNavigateToTests.cs index 89afb185e0e..67ddac8523f 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/NavigateTo/AbstractNavigateToTests.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/NavigateTo/AbstractNavigateToTests.cs @@ -46,25 +46,25 @@ public abstract partial class AbstractNavigateToTests protected INavigateToItemProvider _provider; protected NavigateToTestAggregator _aggregator; - internal static readonly PatternMatch s_emptyExactPatternMatch = new PatternMatch(PatternMatchKind.Exact, true, true, []); - internal static readonly PatternMatch s_emptyPrefixPatternMatch = new PatternMatch(PatternMatchKind.Prefix, true, true, []); - internal static readonly PatternMatch s_emptySubstringPatternMatch = new PatternMatch(PatternMatchKind.Substring, true, true, []); - internal static readonly PatternMatch s_emptyCamelCaseExactPatternMatch = new PatternMatch(PatternMatchKind.CamelCaseExact, true, true, []); - internal static readonly PatternMatch s_emptyCamelCasePrefixPatternMatch = new PatternMatch(PatternMatchKind.CamelCasePrefix, true, true, []); - internal static readonly PatternMatch s_emptyCamelCaseNonContiguousPrefixPatternMatch = new PatternMatch(PatternMatchKind.CamelCaseNonContiguousPrefix, true, true, []); - internal static readonly PatternMatch s_emptyCamelCaseSubstringPatternMatch = new PatternMatch(PatternMatchKind.CamelCaseSubstring, true, true, []); - internal static readonly PatternMatch s_emptyCamelCaseNonContiguousSubstringPatternMatch = new PatternMatch(PatternMatchKind.CamelCaseNonContiguousSubstring, true, true, []); - internal static readonly PatternMatch s_emptyFuzzyPatternMatch = new PatternMatch(PatternMatchKind.Fuzzy, true, true, []); - - internal static readonly PatternMatch s_emptyExactPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.Exact, true, false, []); - internal static readonly PatternMatch s_emptyPrefixPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.Prefix, true, false, []); - internal static readonly PatternMatch s_emptySubstringPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.Substring, true, false, []); - internal static readonly PatternMatch s_emptyCamelCaseExactPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.CamelCaseExact, true, false, []); - internal static readonly PatternMatch s_emptyCamelCasePrefixPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.CamelCasePrefix, true, false, []); - internal static readonly PatternMatch s_emptyCamelCaseNonContiguousPrefixPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.CamelCaseNonContiguousPrefix, true, false, []); - internal static readonly PatternMatch s_emptyCamelCaseSubstringPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.CamelCaseSubstring, true, false, []); - internal static readonly PatternMatch s_emptyCamelCaseNonContiguousSubstringPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.CamelCaseNonContiguousSubstring, true, false, []); - internal static readonly PatternMatch s_emptyFuzzyPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.Fuzzy, true, false, []); + internal static readonly PatternMatch s_emptyExactPatternMatch = new(PatternMatchKind.Exact, true, true, []); + internal static readonly PatternMatch s_emptyPrefixPatternMatch = new(PatternMatchKind.Prefix, true, true, []); + internal static readonly PatternMatch s_emptySubstringPatternMatch = new(PatternMatchKind.Substring, true, true, []); + internal static readonly PatternMatch s_emptyCamelCaseExactPatternMatch = new(PatternMatchKind.CamelCaseExact, true, true, []); + internal static readonly PatternMatch s_emptyCamelCasePrefixPatternMatch = new(PatternMatchKind.CamelCasePrefix, true, true, []); + internal static readonly PatternMatch s_emptyCamelCaseNonContiguousPrefixPatternMatch = new(PatternMatchKind.CamelCaseNonContiguousPrefix, true, true, []); + internal static readonly PatternMatch s_emptyCamelCaseSubstringPatternMatch = new(PatternMatchKind.CamelCaseSubstring, true, true, []); + internal static readonly PatternMatch s_emptyCamelCaseNonContiguousSubstringPatternMatch = new(PatternMatchKind.CamelCaseNonContiguousSubstring, true, true, []); + internal static readonly PatternMatch s_emptyFuzzyPatternMatch = new(PatternMatchKind.Fuzzy, true, true, []); + + internal static readonly PatternMatch s_emptyExactPatternMatch_NotCaseSensitive = new(PatternMatchKind.Exact, true, false, []); + internal static readonly PatternMatch s_emptyPrefixPatternMatch_NotCaseSensitive = new(PatternMatchKind.Prefix, true, false, []); + internal static readonly PatternMatch s_emptySubstringPatternMatch_NotCaseSensitive = new(PatternMatchKind.Substring, true, false, []); + internal static readonly PatternMatch s_emptyCamelCaseExactPatternMatch_NotCaseSensitive = new(PatternMatchKind.CamelCaseExact, true, false, []); + internal static readonly PatternMatch s_emptyCamelCasePrefixPatternMatch_NotCaseSensitive = new(PatternMatchKind.CamelCasePrefix, true, false, []); + internal static readonly PatternMatch s_emptyCamelCaseNonContiguousPrefixPatternMatch_NotCaseSensitive = new(PatternMatchKind.CamelCaseNonContiguousPrefix, true, false, []); + internal static readonly PatternMatch s_emptyCamelCaseSubstringPatternMatch_NotCaseSensitive = new(PatternMatchKind.CamelCaseSubstring, true, false, []); + internal static readonly PatternMatch s_emptyCamelCaseNonContiguousSubstringPatternMatch_NotCaseSensitive = new(PatternMatchKind.CamelCaseNonContiguousSubstring, true, false, []); + internal static readonly PatternMatch s_emptyFuzzyPatternMatch_NotCaseSensitive = new(PatternMatchKind.Fuzzy, true, false, []); protected abstract EditorTestWorkspace CreateWorkspace(string content, TestComposition composition); protected abstract string Language { get; } diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.Callback.cs b/src/roslyn/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.Callback.cs index 96956e50069..6463dd5ea39 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.Callback.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.Callback.cs @@ -18,8 +18,7 @@ private sealed class Callback : INavigateToCallback { private readonly List _itemsReceived = []; - private readonly TaskCompletionSource> _taskCompletionSource = - new TaskCompletionSource>(); + private readonly TaskCompletionSource> _taskCompletionSource = new(); public Callback(INavigateToOptions options) { diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Semantics/SpeculationAnalyzerTestsBase.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Semantics/SpeculationAnalyzerTestsBase.cs index 5cc02d08ec9..ba175205e95 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Semantics/SpeculationAnalyzerTestsBase.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/Semantics/SpeculationAnalyzerTestsBase.cs @@ -16,7 +16,7 @@ public abstract class SpeculationAnalyzerTestsBase : TestBase { protected const string CompilationName = "SemanticModelTestCompilation"; - protected readonly Regex UnderTestRegex = new Regex(@"\[\|(?.*?)\|\]"); + protected readonly Regex UnderTestRegex = new(@"\[\|(?.*?)\|\]"); protected readonly MetadataReference[] References = [ diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/TextEditorFactoryExtensions.cs b/src/roslyn/src/EditorFeatures/TestUtilities/TextEditorFactoryExtensions.cs index 66cbb7f30d6..fef733630f5 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/TextEditorFactoryExtensions.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/TextEditorFactoryExtensions.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests; internal static class TextEditorFactoryExtensions { public static DisposableTextView CreateDisposableTextView(this ITextEditorFactoryService textEditorFactory) - => new DisposableTextView(textEditorFactory.CreateTextView()); + => new(textEditorFactory.CreateTextView()); public static DisposableTextView CreateDisposableTextView(this ITextEditorFactoryService textEditorFactory, ITextBuffer buffer, ImmutableArray roles = default) { diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs b/src/roslyn/src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs index ba3bd615d3f..eec26df43e4 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs @@ -20,7 +20,7 @@ public abstract class AbstractTextStructureNavigatorTests protected abstract string ContentType { get; } protected abstract EditorTestWorkspace CreateWorkspace(string code); - protected StringBuilder result = new StringBuilder(); + protected StringBuilder result = new(); protected void AssertExtent(string code) { diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Threading/WpfTestSharedData.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Threading/WpfTestSharedData.cs index 54743a35181..f985309e37f 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Threading/WpfTestSharedData.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/Threading/WpfTestSharedData.cs @@ -13,7 +13,7 @@ namespace Roslyn.Test.Utilities; public sealed class WpfTestSharedData { - internal static readonly WpfTestSharedData Instance = new WpfTestSharedData(); + internal static readonly WpfTestSharedData Instance = new(); /// /// Holds the last 10 test cases executed: more recent test cases will occur later in the @@ -21,7 +21,7 @@ public sealed class WpfTestSharedData /// private readonly List _recentTestCases = []; - public readonly SemaphoreSlim TestSerializationGate = new SemaphoreSlim(1, 1); + public readonly SemaphoreSlim TestSerializationGate = new(1, 1); private WpfTestSharedData() { diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Utilities/TestCommandExecutionContext.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Utilities/TestCommandExecutionContext.cs index 9ab178aa03c..01bc32e5471 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Utilities/TestCommandExecutionContext.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/Utilities/TestCommandExecutionContext.cs @@ -11,5 +11,5 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities; internal static class TestCommandExecutionContext { public static CommandExecutionContext Create() - => new CommandExecutionContext(new TestUIThreadOperationContext()); + => new(new TestUIThreadOperationContext()); } diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Workspaces/NoCompilationDocumentDiagnosticAnalyzer.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Workspaces/NoCompilationDocumentDiagnosticAnalyzer.cs index 71041e5c854..6958c6eefd1 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Workspaces/NoCompilationDocumentDiagnosticAnalyzer.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/Workspaces/NoCompilationDocumentDiagnosticAnalyzer.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; [DiagnosticAnalyzer(NoCompilationConstants.LanguageName)] internal sealed class NoCompilationDocumentDiagnosticAnalyzer : DocumentDiagnosticAnalyzer { - public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor( + public static readonly DiagnosticDescriptor Descriptor = new( "NC0000", "No Compilation Syntax Error", "No Compilation Syntax Error", "Error", DiagnosticSeverity.Error, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics => [Descriptor]; diff --git a/src/roslyn/src/EditorFeatures/Text/Extensions.TextBufferContainer.cs b/src/roslyn/src/EditorFeatures/Text/Extensions.TextBufferContainer.cs index 4a1a80c825c..0036699df01 100644 --- a/src/roslyn/src/EditorFeatures/Text/Extensions.TextBufferContainer.cs +++ b/src/roslyn/src/EditorFeatures/Text/Extensions.TextBufferContainer.cs @@ -19,7 +19,7 @@ public static partial class Extensions internal sealed class TextBufferContainer : SourceTextContainer { private readonly WeakReference _weakEditorBuffer; - private readonly object _gate = new object(); + private readonly object _gate = new(); private readonly ITextBufferCloneService? _textBufferCloneService; private event EventHandler? EtextChanged; diff --git a/src/roslyn/src/EditorFeatures/Text/Shared/Extensions/ITextSnapshotExtensions.cs b/src/roslyn/src/EditorFeatures/Text/Shared/Extensions/ITextSnapshotExtensions.cs index 7fc5dde2323..04fde7c6de1 100644 --- a/src/roslyn/src/EditorFeatures/Text/Shared/Extensions/ITextSnapshotExtensions.cs +++ b/src/roslyn/src/EditorFeatures/Text/Shared/Extensions/ITextSnapshotExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Text.Shared.Extensions; internal static partial class ITextSnapshotExtensions { public static SnapshotPoint GetPoint(this ITextSnapshot snapshot, int position) - => new SnapshotPoint(snapshot, position); + => new(snapshot, position); public static SnapshotPoint? TryGetPoint(this ITextSnapshot snapshot, int lineNumber, int columnIndex) { @@ -76,13 +76,13 @@ public static bool TryGetPosition(this ITextSnapshot snapshot, int lineNumber, i } public static SnapshotSpan GetSpan(this ITextSnapshot snapshot, int start, int length) - => new SnapshotSpan(snapshot, new Span(start, length)); + => new(snapshot, new Span(start, length)); public static SnapshotSpan GetSpanFromBounds(this ITextSnapshot snapshot, int start, int end) - => new SnapshotSpan(snapshot, Span.FromBounds(start, end)); + => new(snapshot, Span.FromBounds(start, end)); public static SnapshotSpan GetSpan(this ITextSnapshot snapshot, Span span) - => new SnapshotSpan(snapshot, span); + => new(snapshot, span); public static TagSpan GetTagSpan(this ITextSnapshot snapshot, Span span, TTag tag) where TTag : ITag => new(new SnapshotSpan(snapshot, span), tag); diff --git a/src/roslyn/src/EditorFeatures/Text/Shared/Extensions/TextSpanExtensions.cs b/src/roslyn/src/EditorFeatures/Text/Shared/Extensions/TextSpanExtensions.cs index 073ad13544c..a4bab16fcd9 100644 --- a/src/roslyn/src/EditorFeatures/Text/Shared/Extensions/TextSpanExtensions.cs +++ b/src/roslyn/src/EditorFeatures/Text/Shared/Extensions/TextSpanExtensions.cs @@ -13,13 +13,13 @@ internal static class TextSpanExtensions /// Convert a instance to a . /// public static Span ToSpan(this TextSpan textSpan) - => new Span(textSpan.Start, textSpan.Length); + => new(textSpan.Start, textSpan.Length); /// /// Add an offset to a . /// public static TextSpan MoveTo(this TextSpan textSpan, int offset) - => new TextSpan(textSpan.Start + offset, textSpan.Length); + => new(textSpan.Start + offset, textSpan.Length); /// /// Convert a to a on the given instance diff --git a/src/roslyn/src/EditorFeatures/VisualBasicTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.vb b/src/roslyn/src/EditorFeatures/VisualBasicTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.vb index 07bc81522e0..905cc16345e 100644 --- a/src/roslyn/src/EditorFeatures/VisualBasicTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.vb +++ b/src/roslyn/src/EditorFeatures/VisualBasicTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.vb @@ -96,7 +96,7 @@ End Class Private Shared Sub AccessSupportedDiagnostics(analyzer As DiagnosticAnalyzer) Dim diagnosticService = New HostDiagnosticAnalyzers({New AnalyzerImageReference(ImmutableArray.Create(analyzer))}) - diagnosticService.GetDiagnosticDescriptorsPerReference(New DiagnosticAnalyzerInfoCache()) + diagnosticService.GetDiagnosticDescriptorsPerReference(New DiagnosticAnalyzerInfoCache(), project:=Nothing) End Sub diff --git a/src/roslyn/src/EditorFeatures/VisualBasicTest/Formatting/CodeCleanUpTests.vb b/src/roslyn/src/EditorFeatures/VisualBasicTest/Formatting/CodeCleanUpTests.vb index d67ba6133a0..f35ef1e95b0 100644 --- a/src/roslyn/src/EditorFeatures/VisualBasicTest/Formatting/CodeCleanUpTests.vb +++ b/src/roslyn/src/EditorFeatures/VisualBasicTest/Formatting/CodeCleanUpTests.vb @@ -658,7 +658,6 @@ End Class End Function Private Class ModifySolutionFixAll : Inherits FixAllProvider - Public Overrides Function GetSupportedFixAllScopes() As IEnumerable(Of FixAllScope) Return {FixAllScope.Project, FixAllScope.Solution, FixAllScope.Custom} End Function diff --git a/src/roslyn/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs b/src/roslyn/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs index 7a7db603929..ab5449d9c05 100644 --- a/src/roslyn/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs +++ b/src/roslyn/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs @@ -22,7 +22,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; using static Microsoft.CodeAnalysis.CSharp.AddImport.AddImportDiagnosticIds; namespace Microsoft.CodeAnalysis.CSharp.AddImport; diff --git a/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.FixAllProvider.cs b/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.FixAllProvider.cs index d88a6969b11..839ef19d05b 100644 --- a/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.FixAllProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.FixAllProvider.cs @@ -11,29 +11,28 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Shared.Extensions; -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.EnableNullable; internal sealed partial class EnableNullableCodeRefactoringProvider : CodeRefactoringProvider { - internal sealed override CodeAnalysis.CodeRefactorings.FixAllProvider? GetFixAllProvider() - => FixAllProvider.Instance; + public sealed override CodeAnalysis.CodeRefactorings.RefactorAllProvider? GetRefactorAllProvider() + => RefactorAllProvider.Instance; - private sealed class FixAllProvider : CodeAnalysis.CodeRefactorings.FixAllProvider + private sealed class RefactorAllProvider : CodeAnalysis.CodeRefactorings.RefactorAllProvider { - public static readonly FixAllProvider Instance = new(); + public static readonly RefactorAllProvider Instance = new(); - private FixAllProvider() + private RefactorAllProvider() { } - public override IEnumerable GetSupportedFixAllScopes() - => [FixAllScope.Solution]; + public override IEnumerable GetSupportedRefactorAllScopes() + => [RefactorAllScope.Solution]; - public override Task GetFixAsync(FixAllContext fixAllContext) + public override Task GetRefactoringAsync(RefactorAllContext fixAllContext) { - Debug.Assert(fixAllContext.Scope == FixAllScope.Solution); + Debug.Assert(fixAllContext.Scope == RefactorAllScope.Solution); return Task.FromResult(new FixAllCodeAction(EnableNullableReferenceTypesInSolutionAsync)); async Task EnableNullableReferenceTypesInSolutionAsync( diff --git a/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsCodeRefactoringProvider.cs b/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsCodeRefactoringProvider.cs index 2c7b9b48bb0..ca1c863dbf3 100644 --- a/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsCodeRefactoringProvider.cs @@ -41,7 +41,7 @@ public UseRecursivePatternsCodeRefactoringProvider() { } - protected override ImmutableArray SupportedFixAllScopes => AllFixAllScopes; + protected override ImmutableArray SupportedRefactorAllScopes => AllRefactorAllScopes; public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { @@ -579,7 +579,7 @@ when canConvertToSubpattern(name, arg) && !memberAccess.Expression.IsKind(Syntax } } - protected override async Task FixAllAsync( + protected override async Task RefactorAllAsync( Document document, ImmutableArray fixAllSpans, SyntaxEditor editor, diff --git a/src/roslyn/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs b/src/roslyn/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs index a1f8ccb3364..bb3e55ae2a3 100644 --- a/src/roslyn/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs @@ -29,8 +29,8 @@ namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace; [method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] internal sealed class ConvertNamespaceCodeRefactoringProvider() : SyntaxEditorBasedCodeRefactoringProvider { - protected override ImmutableArray SupportedFixAllScopes - => [FixAllScope.Project, FixAllScope.Solution]; + protected override ImmutableArray SupportedRefactorAllScopes + => [RefactorAllScope.Project, RefactorAllScope.Solution]; public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { @@ -84,7 +84,7 @@ private static bool IsValidPosition(BaseNamespaceDeclarationSyntax baseDeclarati throw ExceptionUtilities.UnexpectedValue(baseDeclaration.Kind()); } - protected override async Task FixAllAsync( + protected override async Task RefactorAllAsync( Document document, ImmutableArray fixAllSpans, SyntaxEditor editor, diff --git a/src/roslyn/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionCodeRefactoringProvider.cs b/src/roslyn/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionCodeRefactoringProvider.cs index 2f87820f961..0fce949f152 100644 --- a/src/roslyn/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionCodeRefactoringProvider.cs @@ -57,8 +57,8 @@ public override int GetHashCode() => ExtensionMethodEqualityComparer.Instance.GetHashCode(this); } - internal override FixAllProvider? GetFixAllProvider() - => new ConvertToExtensionFixAllProvider(); + public override RefactorAllProvider? GetRefactorAllProvider() + => new ConvertToExtensionRefactorAllProvider(); private static ExtensionMethodInfo? TryGetExtensionMethodInfo( SemanticModel semanticModel, diff --git a/src/roslyn/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionFixAllProvider.cs b/src/roslyn/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionFixAllProvider.cs index bf092c673fa..8ab5ed63b7f 100644 --- a/src/roslyn/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionFixAllProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionFixAllProvider.cs @@ -21,16 +21,16 @@ namespace Microsoft.CodeAnalysis.CSharp.ConvertToExtension; internal sealed partial class ConvertToExtensionCodeRefactoringProvider { - private sealed class ConvertToExtensionFixAllProvider() - : DocumentBasedFixAllProvider( - [FixAllScope.Document, FixAllScope.Project, FixAllScope.Solution, FixAllScope.ContainingType]) + private sealed class ConvertToExtensionRefactorAllProvider() + : DocumentBasedRefactorAllProvider( + [RefactorAllScope.Document, RefactorAllScope.Project, RefactorAllScope.Solution, RefactorAllScope.ContainingType]) { - protected override async Task FixAllAsync( - FixAllContext fixAllContext, + protected override async Task RefactorAllAsync( + RefactorAllContext refactorAllContext, Document document, - Optional> fixAllSpans) + Optional> refactorAllSpans) { - var cancellationToken = fixAllContext.CancellationToken; + var cancellationToken = refactorAllContext.CancellationToken; var codeGenerationService = (CSharpCodeGenerationService)document.GetRequiredLanguageService(); @@ -38,7 +38,7 @@ private sealed class ConvertToExtensionFixAllProvider() var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var editor = new SyntaxEditor(root, document.Project.Solution.Services); - foreach (var declaration in GetTopLevelClassDeclarations(root, fixAllSpans)) + foreach (var declaration in GetTopLevelClassDeclarations(root, refactorAllSpans)) { // We might hit partial parts that have no extension methods in them. Just skip those. var extensionMethods = GetAllExtensionMethods(semanticModel, declaration, cancellationToken); diff --git a/src/roslyn/src/Features/CSharp/Portable/ConvertToRawString/ConvertStringToRawStringCodeRefactoringProvider.cs b/src/roslyn/src/Features/CSharp/Portable/ConvertToRawString/ConvertStringToRawStringCodeRefactoringProvider.cs index a4c11272512..c7465e97421 100644 --- a/src/roslyn/src/Features/CSharp/Portable/ConvertToRawString/ConvertStringToRawStringCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/ConvertToRawString/ConvertStringToRawStringCodeRefactoringProvider.cs @@ -41,7 +41,7 @@ static ConvertStringToRawStringCodeRefactoringProvider() private static readonly ImmutableArray s_convertStringProviders = [ConvertRegularStringToRawStringProvider.Instance, ConvertInterpolatedStringToRawStringProvider.Instance]; - protected override ImmutableArray SupportedFixAllScopes => AllFixAllScopes; + protected override ImmutableArray SupportedRefactorAllScopes => AllRefactorAllScopes; protected override CodeActionCleanup Cleanup => CodeActionCleanup.SyntaxOnly; @@ -139,7 +139,7 @@ private static async Task UpdateDocumentAsync( return document.WithSyntaxRoot(root.ReplaceNode(expression, replacement)); } - protected override async Task FixAllAsync( + protected override async Task RefactorAllAsync( Document document, ImmutableArray fixAllSpans, SyntaxEditor editor, diff --git a/src/roslyn/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs b/src/roslyn/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs index 6001081d453..ba512044830 100644 --- a/src/roslyn/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs +++ b/src/roslyn/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs @@ -1665,7 +1665,7 @@ public static IEnumerable GetSequenceEdits(ImmutableArray { - internal static readonly LcsTokens Instance = new LcsTokens(); + internal static readonly LcsTokens Instance = new(); protected override bool Equals(SyntaxToken oldElement, SyntaxToken newElement) => SyntaxFactory.AreEquivalent(oldElement, newElement); @@ -1673,7 +1673,7 @@ protected override bool Equals(SyntaxToken oldElement, SyntaxToken newElement) private sealed class LcsNodes : LongestCommonImmutableArraySubsequence { - internal static readonly LcsNodes Instance = new LcsNodes(); + internal static readonly LcsNodes Instance = new(); protected override bool Equals(SyntaxNode oldElement, SyntaxNode newElement) => SyntaxFactory.AreEquivalent(oldElement, newElement); diff --git a/src/roslyn/src/Features/CSharp/Portable/EncapsulateField/CSharpEncapsulateFieldService.cs b/src/roslyn/src/Features/CSharp/Portable/EncapsulateField/CSharpEncapsulateFieldService.cs index 13a953ca4de..f2b2669799a 100644 --- a/src/roslyn/src/Features/CSharp/Portable/EncapsulateField/CSharpEncapsulateFieldService.cs +++ b/src/roslyn/src/Features/CSharp/Portable/EncapsulateField/CSharpEncapsulateFieldService.cs @@ -130,7 +130,7 @@ protected override async Task> GetFieldsAsync(Docum .OfType() .Where(n => n.Span.IntersectsWith(span)); - var declarations = fields.Where(CanEncapsulate).Select(f => f.Declaration); + var declarations = fields.SelectAsArray(CanEncapsulate, f => f.Declaration); IEnumerable declarators; if (span.IsEmpty) @@ -144,9 +144,10 @@ protected override async Task> GetFieldsAsync(Docum declarators = declarations.SelectMany(d => d.Variables.Where(v => v.Span.IntersectsWith(span))); } - return [.. declarators.Select(d => semanticModel.GetDeclaredSymbol(d, cancellationToken) as IFieldSymbol) - .WhereNotNull() - .Where(f => f.Name.Length != 0)]; + return [.. declarators + .Select(d => semanticModel.GetDeclaredSymbol(d, cancellationToken) as IFieldSymbol) + .WhereNotNull() + .Where(f => f.Name.Length != 0)]; } private bool CanEncapsulate(FieldDeclarationSyntax field) diff --git a/src/roslyn/src/Features/CSharp/Portable/ExternalAccess/Pythia/Api/PythiaSignatureHelpItemWrapper.cs b/src/roslyn/src/Features/CSharp/Portable/ExternalAccess/Pythia/Api/PythiaSignatureHelpItemWrapper.cs index deb42a0126c..5fbac117c5f 100644 --- a/src/roslyn/src/Features/CSharp/Portable/ExternalAccess/Pythia/Api/PythiaSignatureHelpItemWrapper.cs +++ b/src/roslyn/src/Features/CSharp/Portable/ExternalAccess/Pythia/Api/PythiaSignatureHelpItemWrapper.cs @@ -13,7 +13,7 @@ internal readonly struct PythiaSignatureHelpItemWrapper(SignatureHelpItem underl internal readonly SignatureHelpItem UnderlyingObject = underlyingObject; public static SymbolDisplayPart CreateTextDisplayPart(string text) - => new SymbolDisplayPart(SymbolDisplayPartKind.Text, null, text); + => new(SymbolDisplayPartKind.Text, null, text); public static PythiaSignatureHelpItemWrapper CreateFromMethodGroupMethod( Document document, @@ -21,5 +21,5 @@ public static PythiaSignatureHelpItemWrapper CreateFromMethodGroupMethod( int position, SemanticModel semanticModel, IList descriptionParts) - => new PythiaSignatureHelpItemWrapper(AbstractOrdinaryMethodSignatureHelpProvider.ConvertMethodGroupMethod(document, method, position, semanticModel, descriptionParts)); + => new(AbstractOrdinaryMethodSignatureHelpProvider.ConvertMethodGroupMethod(document, method, position, semanticModel, descriptionParts)); } diff --git a/src/roslyn/src/Features/CSharp/Portable/ExtractMethod/CSharpSyntaxTriviaService.cs b/src/roslyn/src/Features/CSharp/Portable/ExtractMethod/CSharpSyntaxTriviaService.cs index 6da63b6307e..ecd8a5ff555 100644 --- a/src/roslyn/src/Features/CSharp/Portable/ExtractMethod/CSharpSyntaxTriviaService.cs +++ b/src/roslyn/src/Features/CSharp/Portable/ExtractMethod/CSharpSyntaxTriviaService.cs @@ -8,7 +8,7 @@ namespace Microsoft.CodeAnalysis.CSharp.ExtractMethod; internal sealed class CSharpSyntaxTriviaService : AbstractSyntaxTriviaService { - public static readonly CSharpSyntaxTriviaService Instance = new CSharpSyntaxTriviaService(); + public static readonly CSharpSyntaxTriviaService Instance = new(); private CSharpSyntaxTriviaService() : base((int)SyntaxKind.EndOfLineTrivia) diff --git a/src/roslyn/src/Features/CSharp/Portable/QuickInfo/CSharpDiagnosticAnalyzerQuickInfoProvider.cs b/src/roslyn/src/Features/CSharp/Portable/QuickInfo/CSharpDiagnosticAnalyzerQuickInfoProvider.cs index 52486fc2ec5..c13f4d3f7e2 100644 --- a/src/roslyn/src/Features/CSharp/Portable/QuickInfo/CSharpDiagnosticAnalyzerQuickInfoProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/QuickInfo/CSharpDiagnosticAnalyzerQuickInfoProvider.cs @@ -25,17 +25,17 @@ namespace Microsoft.CodeAnalysis.CSharp.QuickInfo; [ExtensionOrder(Before = QuickInfoProviderNames.Semantic)] [method: ImportingConstructor] [method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] -internal sealed class CSharpDiagnosticAnalyzerQuickInfoProvider(DiagnosticAnalyzerInfoCache.SharedGlobalCache globalCache) : CommonQuickInfoProvider +internal sealed class CSharpDiagnosticAnalyzerQuickInfoProvider() : CommonQuickInfoProvider { - private readonly DiagnosticAnalyzerInfoCache _diagnosticAnalyzerInfoCache = globalCache.AnalyzerInfoCache; - protected override async Task BuildQuickInfoAsync( QuickInfoContext context, SyntaxToken token) { var document = context.Document; - return GetQuickinfoForPragmaWarning(document, token) ?? - (await GetQuickInfoForSuppressMessageAttributeAsync(document, token, context.CancellationToken).ConfigureAwait(false)); + var cancellationToken = context.CancellationToken; + return + await GetQuickinfoForPragmaWarningAsync(document, token, cancellationToken).ConfigureAwait(false) ?? + await GetQuickInfoForSuppressMessageAttributeAsync(document, token, cancellationToken).ConfigureAwait(false); } protected override Task BuildQuickInfoAsync( @@ -47,7 +47,8 @@ internal sealed class CSharpDiagnosticAnalyzerQuickInfoProvider(DiagnosticAnalyz return Task.FromResult(null); } - private QuickInfoItem? GetQuickinfoForPragmaWarning(Document document, SyntaxToken token) + private static async Task GetQuickinfoForPragmaWarningAsync( + Document document, SyntaxToken token, CancellationToken cancellationToken) { var errorCodeNode = token.Parent switch { @@ -83,10 +84,11 @@ PragmaWarningDirectiveTriviaSyntax directive return null; } - return GetQuickInfoFromSupportedDiagnosticsOfProjectAnalyzers(document, errorCode, errorCodeNode.Span); + return await GetQuickInfoFromSupportedDiagnosticsOfProjectAnalyzersAsync( + document, errorCode, errorCodeNode.Span, cancellationToken).ConfigureAwait(false); } - private async Task GetQuickInfoForSuppressMessageAttributeAsync( + private static async Task GetQuickInfoForSuppressMessageAttributeAsync( Document document, SyntaxToken token, CancellationToken cancellationToken) @@ -121,19 +123,22 @@ PragmaWarningDirectiveTriviaSyntax directive if (checkIdObject.HasValue && checkIdObject.Value is string checkId) { var errorCode = checkId.ExtractErrorCodeFromCheckId(); - return GetQuickInfoFromSupportedDiagnosticsOfProjectAnalyzers(document, errorCode, suppressMessageCheckIdArgument.Span); + return await GetQuickInfoFromSupportedDiagnosticsOfProjectAnalyzersAsync( + document, errorCode, suppressMessageCheckIdArgument.Span, cancellationToken).ConfigureAwait(false); } } return null; } - private QuickInfoItem? GetQuickInfoFromSupportedDiagnosticsOfProjectAnalyzers(Document document, - string errorCode, TextSpan location) + private static async Task GetQuickInfoFromSupportedDiagnosticsOfProjectAnalyzersAsync( + Document document, string errorCode, TextSpan location, CancellationToken cancellationToken) { var hostAnalyzers = document.Project.Solution.SolutionState.Analyzers; - var groupedDiagnostics = hostAnalyzers.GetDiagnosticDescriptorsPerReference(_diagnosticAnalyzerInfoCache, document.Project).Values; - var supportedDiagnostics = groupedDiagnostics.SelectMany(d => d); + var service = document.Project.Solution.Services.GetRequiredService(); + var groupedDiagnostics = await service.GetDiagnosticDescriptorsPerReferenceAsync( + document.Project, cancellationToken).ConfigureAwait(false); + var supportedDiagnostics = groupedDiagnostics.Values.SelectMany(d => d); var diagnosticDescriptor = supportedDiagnostics.FirstOrDefault(d => d.Id == errorCode); if (diagnosticDescriptor != null) { diff --git a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractCSharpSignatureHelpProvider.cs b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractCSharpSignatureHelpProvider.cs index 3b4adc4a334..48a9e6f0238 100644 --- a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractCSharpSignatureHelpProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractCSharpSignatureHelpProvider.cs @@ -16,22 +16,22 @@ internal abstract partial class AbstractCSharpSignatureHelpProvider : AbstractSi .AddMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.AllowDefaultLiteral); protected static SymbolDisplayPart Keyword(SyntaxKind kind) - => new SymbolDisplayPart(SymbolDisplayPartKind.Keyword, null, SyntaxFacts.GetText(kind)); + => new(SymbolDisplayPartKind.Keyword, null, SyntaxFacts.GetText(kind)); protected static SymbolDisplayPart Operator(SyntaxKind kind) - => new SymbolDisplayPart(SymbolDisplayPartKind.Operator, null, SyntaxFacts.GetText(kind)); + => new(SymbolDisplayPartKind.Operator, null, SyntaxFacts.GetText(kind)); protected static SymbolDisplayPart Punctuation(SyntaxKind kind) - => new SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, null, SyntaxFacts.GetText(kind)); + => new(SymbolDisplayPartKind.Punctuation, null, SyntaxFacts.GetText(kind)); protected static SymbolDisplayPart Text(string text) - => new SymbolDisplayPart(SymbolDisplayPartKind.Text, null, text); + => new(SymbolDisplayPartKind.Text, null, text); protected static SymbolDisplayPart Space() - => new SymbolDisplayPart(SymbolDisplayPartKind.Space, null, " "); + => new(SymbolDisplayPartKind.Space, null, " "); protected static SymbolDisplayPart NewLine() - => new SymbolDisplayPart(SymbolDisplayPartKind.LineBreak, null, "\r\n"); + => new(SymbolDisplayPartKind.LineBreak, null, "\r\n"); private static readonly IList _separatorParts = [ diff --git a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AttributeSignatureHelpProvider.cs b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AttributeSignatureHelpProvider.cs index a84a014f940..bb27965b2b0 100644 --- a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AttributeSignatureHelpProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AttributeSignatureHelpProvider.cs @@ -176,7 +176,7 @@ private static IList GetParameters( var displayParts = new List { - new SymbolDisplayPart( + new( namedParameter is IFieldSymbol ? SymbolDisplayPartKind.FieldName : SymbolDisplayPartKind.PropertyName, namedParameter, namedParameter.Name.ToIdentifierToken().ToString()), Space(), diff --git a/src/roslyn/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs b/src/roslyn/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs index 6d0e667a3be..72cb2ec588c 100644 --- a/src/roslyn/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs @@ -55,7 +55,7 @@ internal sealed class UseExpressionBodyCodeRefactoringProvider() : SyntaxEditorB } } - protected override ImmutableArray SupportedFixAllScopes => AllFixAllScopes; + protected override ImmutableArray SupportedRefactorAllScopes => AllRefactorAllScopes; public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { @@ -184,7 +184,7 @@ private static SyntaxNode GetUpdatedRoot( return root.ReplaceNode(parent, updatedParent); } - protected override async Task FixAllAsync( + protected override async Task RefactorAllAsync( Document document, ImmutableArray fixAllSpans, SyntaxEditor editor, diff --git a/src/roslyn/src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests_OptionSets.cs b/src/roslyn/src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests_OptionSets.cs index 6ab1e4fde3d..d1172e63e39 100644 --- a/src/roslyn/src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests_OptionSets.cs +++ b/src/roslyn/src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests_OptionSets.cs @@ -19,51 +19,51 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertAutoPropertyToFu public partial class ConvertAutoPropertyToFullPropertyTests { private OptionsCollection PreferExpressionBodiedAccessorsWhenPossible - => new OptionsCollection(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithSuggestionEnforcement } }; + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithSuggestionEnforcement } }; private OptionsCollection PreferExpressionBodiedAccessorsWhenOnSingleLine - => new OptionsCollection(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenOnSingleLineWithSilentEnforcement } }; + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenOnSingleLineWithSilentEnforcement } }; private OptionsCollection DoNotPreferExpressionBodiedAccessors - => new OptionsCollection(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement } }; + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement } }; private OptionsCollection DoNotPreferExpressionBodiedAccessorsAndPropertyOpenBraceOnSameLine - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, { CSharpFormattingOptions2.NewLineBeforeOpenBrace, NewLineBeforeOpenBracePlacement.All & ~NewLineBeforeOpenBracePlacement.Properties }, }; private OptionsCollection DoNotPreferExpressionBodiedAccessorsAndAccessorOpenBraceOnSameLine - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, { CSharpFormattingOptions2.NewLineBeforeOpenBrace, NewLineBeforeOpenBracePlacement.All & ~NewLineBeforeOpenBracePlacement.Accessors }, }; private OptionsCollection PreferExpressionBodiesOnAccessorsAndMethods - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, }; private OptionsCollection UseCustomFieldName - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { NamingStyleOptions.NamingPreferences, CreateCustomFieldNamingStylePreference() }, { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, }; private OptionsCollection UseUnderscorePrefixedFieldName - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { NamingStyleOptions.NamingPreferences, CreateUnderscorePrefixedFieldNamingStylePreference() }, { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, }; private OptionsCollection UseCustomStaticFieldName - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { NamingStyleOptions.NamingPreferences, CreateCustomStaticFieldNamingStylePreference() }, { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, diff --git a/src/roslyn/src/Features/CSharpTest/ConvertForEachToFor/ConvertForEachToForTests.cs b/src/roslyn/src/Features/CSharpTest/ConvertForEachToFor/ConvertForEachToForTests.cs index 6d4a3be03c8..8f905c6bae2 100644 --- a/src/roslyn/src/Features/CSharpTest/ConvertForEachToFor/ConvertForEachToForTests.cs +++ b/src/roslyn/src/Features/CSharpTest/ConvertForEachToFor/ConvertForEachToForTests.cs @@ -24,7 +24,7 @@ public sealed partial class ConvertForEachToForTests : AbstractCSharpCodeActionT protected override CodeRefactoringProvider CreateCodeRefactoringProvider(TestWorkspace workspace, TestParameters parameters) => new CSharpConvertForEachToForCodeRefactoringProvider(); - private readonly CodeStyleOption2 onWithSilent = new CodeStyleOption2(true, NotificationOption2.Silent); + private readonly CodeStyleOption2 onWithSilent = new(true, NotificationOption2.Silent); private OptionsCollection ImplicitTypeEverywhere => new(GetLanguage()) diff --git a/src/roslyn/src/Features/CSharpTest/ConvertForToForEach/ConvertForToForEachTests.cs b/src/roslyn/src/Features/CSharpTest/ConvertForToForEach/ConvertForToForEachTests.cs index 8168a715116..1b228a291ff 100644 --- a/src/roslyn/src/Features/CSharpTest/ConvertForToForEach/ConvertForToForEachTests.cs +++ b/src/roslyn/src/Features/CSharpTest/ConvertForToForEach/ConvertForToForEachTests.cs @@ -22,10 +22,10 @@ public sealed class ConvertForToForEachTests : AbstractCSharpCodeActionTest_NoEd protected override CodeRefactoringProvider CreateCodeRefactoringProvider(TestWorkspace workspace, TestParameters parameters) => new CSharpConvertForToForEachCodeRefactoringProvider(); - private readonly CodeStyleOption2 onWithSilent = new CodeStyleOption2(true, NotificationOption2.Silent); + private readonly CodeStyleOption2 onWithSilent = new(true, NotificationOption2.Silent); private OptionsCollection ImplicitTypeEverywhere() - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.VarElsewhere, onWithSilent }, { CSharpCodeStyleOptions.VarWhenTypeIsApparent, onWithSilent }, diff --git a/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/AllAnalyzersSeverityConfigurationTests.cs b/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/AllAnalyzersSeverityConfigurationTests.cs index 60033709f3b..efd13f961d7 100644 --- a/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/AllAnalyzersSeverityConfigurationTests.cs +++ b/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/AllAnalyzersSeverityConfigurationTests.cs @@ -22,7 +22,7 @@ public abstract partial class AllAnalyzersSeverityConfigurationTests : AbstractS { private sealed class CustomDiagnosticAnalyzer : DiagnosticAnalyzer { - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + private static readonly DiagnosticDescriptor Rule = new( id: "XYZ0001", title: "Title", messageFormat: "Message", diff --git a/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/CategoryBasedSeverityConfigurationTests.cs b/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/CategoryBasedSeverityConfigurationTests.cs index b4e367a7025..4b15d6504fc 100644 --- a/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/CategoryBasedSeverityConfigurationTests.cs +++ b/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/CategoryBasedSeverityConfigurationTests.cs @@ -22,7 +22,7 @@ public abstract partial class CategoryBasedSeverityConfigurationTests : Abstract { private sealed class CustomDiagnosticAnalyzer : DiagnosticAnalyzer { - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + private static readonly DiagnosticDescriptor Rule = new( id: "XYZ0001", title: "Title", messageFormat: "Message", diff --git a/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/DotNetDiagnosticSeverityBasedSeverityConfigurationTests.cs b/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/DotNetDiagnosticSeverityBasedSeverityConfigurationTests.cs index 576b46278a0..f1d5950ecf1 100644 --- a/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/DotNetDiagnosticSeverityBasedSeverityConfigurationTests.cs +++ b/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/DotNetDiagnosticSeverityBasedSeverityConfigurationTests.cs @@ -22,7 +22,7 @@ public abstract partial class DotNetDiagnosticSeverityBasedSeverityConfiguration { private sealed class CustomDiagnosticAnalyzer : DiagnosticAnalyzer { - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + private static readonly DiagnosticDescriptor Rule = new( id: "XYZ0001", title: "Title", messageFormat: "Message", diff --git a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/RemoveSuppressionTests.cs b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/RemoveSuppressionTests.cs index c875ec80500..5463bda2728 100644 --- a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/RemoveSuppressionTests.cs +++ b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/RemoveSuppressionTests.cs @@ -27,7 +27,7 @@ protected sealed class UserDiagnosticAnalyzer : DiagnosticAnalyzer { private readonly bool _reportDiagnosticsWithoutLocation; public static readonly DiagnosticDescriptor Decsciptor = - new DiagnosticDescriptor("InfoDiagnostic", "InfoDiagnostic Title", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); + new("InfoDiagnostic", "InfoDiagnostic Title", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { diff --git a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/RemoveUnnecessaryPragmaSuppressionsTests.cs b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/RemoveUnnecessaryPragmaSuppressionsTests.cs index 4077ff2f4b1..9d99b7cab68 100644 --- a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/RemoveUnnecessaryPragmaSuppressionsTests.cs +++ b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/RemoveUnnecessaryPragmaSuppressionsTests.cs @@ -49,9 +49,9 @@ protected override TestParameters SetParameterDefaults(TestParameters parameters protected sealed class UserDiagnosticAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor Descriptor0168 = - new DiagnosticDescriptor("Analyzer0168", "Variable is declared but never used", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true); + new("Analyzer0168", "Variable is declared but never used", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor Descriptor0219 = - new DiagnosticDescriptor("Analyzer0219", "Variable is assigned but its value is never used", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true); + new("Analyzer0219", "Variable is assigned but its value is never used", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics => [Descriptor0168, Descriptor0219]; @@ -106,7 +106,7 @@ public override void Initialize(AnalysisContext context) protected sealed class CompilationEndDiagnosticAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor Descriptor = - new DiagnosticDescriptor("CompilationEndId", "Title", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true, + new("CompilationEndId", "Title", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true, customTags: [WellKnownDiagnosticTags.CompilationEnd]); public override ImmutableArray SupportedDiagnostics => [Descriptor]; public override void Initialize(AnalysisContext context) diff --git a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionAllCodeTests.cs b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionAllCodeTests.cs index e0e17228856..3bf87eeb578 100644 --- a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionAllCodeTests.cs +++ b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionAllCodeTests.cs @@ -28,7 +28,7 @@ protected override TestWorkspace CreateWorkspaceFromFile(string definition, Pars => TestWorkspace.CreateCSharp(definition, (CSharpParseOptions)parseOptions, composition: s_compositionWithMockDiagnosticUpdateSourceRegistrationService); internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) - => new Tuple(new Analyzer(), new CSharpSuppressionCodeFixProvider()); + => new(new Analyzer(), new CSharpSuppressionCodeFixProvider()); [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1007071")] [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/956453")] diff --git a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTest_FixMultipleTests.cs b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTest_FixMultipleTests.cs index 86fb78b7173..b2d432cc4d6 100644 --- a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTest_FixMultipleTests.cs +++ b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTest_FixMultipleTests.cs @@ -36,9 +36,9 @@ internal override Tuple CreateDia private sealed class UserDiagnosticAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor Decsciptor1 = - new DiagnosticDescriptor("InfoDiagnostic", "InfoDiagnostic Title", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); + new("InfoDiagnostic", "InfoDiagnostic Title", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); public static readonly DiagnosticDescriptor Decsciptor2 = - new DiagnosticDescriptor("InfoDiagnostic2", "InfoDiagnostic2 Title", "InfoDiagnostic2", "InfoDiagnostic2", DiagnosticSeverity.Info, isEnabledByDefault: true); + new("InfoDiagnostic2", "InfoDiagnostic2 Title", "InfoDiagnostic2", "InfoDiagnostic2", DiagnosticSeverity.Info, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics => [Decsciptor1, Decsciptor2]; diff --git a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs index 090d130d7cb..e85e30f06f9 100644 --- a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs +++ b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs @@ -492,7 +492,7 @@ void Method() var allFixes = (await fixService.GetFixesAsync(document, span, CancellationToken.None)) .SelectMany(fixCollection => fixCollection.Fixes); - var cs0219Fixes = allFixes.Where(fix => fix.PrimaryDiagnostic.Id == "CS0219").ToArray(); + var cs0219Fixes = allFixes.Where(fix => fix.Diagnostics.First().Id == "CS0219").ToArray(); // Ensure that there are no duplicate suppression fixes. Assert.Equal(1, cs0219Fixes.Length); @@ -502,7 +502,7 @@ void Method() // Ensure that there *is* a fix for the other warning and that it has a *different* // equivalence key so that it *doesn't* get de-duplicated Assert.Equal(1, diagnostics.Where(d => d.Id == "CS0168").Count()); - var cs0168Fixes = allFixes.Where(fix => fix.PrimaryDiagnostic.Id == "CS0168"); + var cs0168Fixes = allFixes.Where(fix => fix.Diagnostics.First().Id == "CS0168"); var cs0168EquivalenceKey = cs0168Fixes.Single().Action.EquivalenceKey; Assert.NotNull(cs0168EquivalenceKey); Assert.NotEqual(cs0219EquivalenceKey, cs0168EquivalenceKey); @@ -731,7 +731,7 @@ public sealed partial class UserInfoDiagnosticSuppressionTests : CSharpPragmaWar private sealed class UserDiagnosticAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor Decsciptor = - new DiagnosticDescriptor("InfoDiagnostic", "InfoDiagnostic Title", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); + new("InfoDiagnostic", "InfoDiagnostic Title", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { @@ -845,7 +845,7 @@ public sealed class UserErrorDiagnosticSuppressionTests : CSharpPragmaWarningDis private sealed class UserDiagnosticAnalyzer : DiagnosticAnalyzer { private readonly DiagnosticDescriptor _descriptor = - new DiagnosticDescriptor("ErrorDiagnostic", "ErrorDiagnostic", "ErrorDiagnostic", "ErrorDiagnostic", DiagnosticSeverity.Error, isEnabledByDefault: true); + new("ErrorDiagnostic", "ErrorDiagnostic", "ErrorDiagnostic", "ErrorDiagnostic", DiagnosticSeverity.Error, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { @@ -910,7 +910,7 @@ public sealed class DiagnosticWithBadIdSuppressionTests : CSharpPragmaWarningDis private sealed class UserDiagnosticAnalyzer : DiagnosticAnalyzer { private readonly DiagnosticDescriptor _descriptor = - new DiagnosticDescriptor("@~DiagnosticWithBadId", "DiagnosticWithBadId", "DiagnosticWithBadId", "DiagnosticWithBadId", DiagnosticSeverity.Info, isEnabledByDefault: true); + new("@~DiagnosticWithBadId", "DiagnosticWithBadId", "DiagnosticWithBadId", "DiagnosticWithBadId", DiagnosticSeverity.Info, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { @@ -959,7 +959,7 @@ public sealed partial class MultilineDiagnosticSuppressionTests : CSharpPragmaWa private sealed class UserDiagnosticAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor Decsciptor = - new DiagnosticDescriptor("InfoDiagnostic", "InfoDiagnostic Title", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); + new("InfoDiagnostic", "InfoDiagnostic Title", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { diff --git a/src/roslyn/src/Features/CSharpTest/EditAndContinue/StatementMatchingTests.cs b/src/roslyn/src/Features/CSharpTest/EditAndContinue/StatementMatchingTests.cs index 68b9e7d3076..d10d41086e7 100644 --- a/src/roslyn/src/Features/CSharpTest/EditAndContinue/StatementMatchingTests.cs +++ b/src/roslyn/src/Features/CSharpTest/EditAndContinue/StatementMatchingTests.cs @@ -37,7 +37,7 @@ public void KnownMatches() var knownMatches = new KeyValuePair[] { - new KeyValuePair(((BlockSyntax)m1.RootNodes.First()).Statements[1], ((BlockSyntax)m2.RootNodes.First()).Statements[0]) + new(((BlockSyntax)m1.RootNodes.First()).Statements[1], ((BlockSyntax)m2.RootNodes.First()).Statements[0]) }; // pre-matched: diff --git a/src/roslyn/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs b/src/roslyn/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs index 2f00d6ba5a1..2b290ac6239 100644 --- a/src/roslyn/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs +++ b/src/roslyn/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs @@ -28,7 +28,7 @@ protected override CodeRefactoringProvider CreateCodeRefactoringProvider(TestWor protected override ImmutableArray MassageActions(ImmutableArray actions) => GetNestedActions(actions); - private readonly CodeStyleOption2 onWithInfo = new CodeStyleOption2(true, NotificationOption2.Suggestion); + private readonly CodeStyleOption2 onWithInfo = new(true, NotificationOption2.Suggestion); // specify all options explicitly to override defaults. private OptionsCollection ImplicitTypingEverywhere() diff --git a/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyFixAllTests.cs b/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyFixAllTests.cs index de64831213a..6efb041ed53 100644 --- a/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyFixAllTests.cs +++ b/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyFixAllTests.cs @@ -23,7 +23,7 @@ private OptionsCollection UseBlockBody => this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.NeverWithSilentEnforcement); private OptionsCollection UseBlockBodyForMethodsAndAccessorsAndProperties - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, diff --git a/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs b/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs index e8ca5722864..51ec5b32a06 100644 --- a/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs +++ b/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs @@ -24,56 +24,56 @@ protected override CodeRefactoringProvider CreateCodeRefactoringProvider(TestWor => new UseExpressionBodyCodeRefactoringProvider(); private OptionsCollection UseExpressionBodyForAccessors_BlockBodyForProperties - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, }; private OptionsCollection UseExpressionBodyForAccessors_BlockBodyForProperties_DisabledDiagnostic - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, ExpressionBodyPreference.WhenPossible, NotificationOption2.None }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, ExpressionBodyPreference.Never, NotificationOption2.None }, }; private OptionsCollection UseExpressionBodyForAccessors_ExpressionBodyForProperties - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, }; private OptionsCollection UseExpressionBodyForAccessors_ExpressionBodyForProperties_DisabledDiagnostic - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, ExpressionBodyPreference.WhenPossible, NotificationOption2.None }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, ExpressionBodyPreference.WhenPossible, NotificationOption2.None }, }; private OptionsCollection UseBlockBodyForAccessors_ExpressionBodyForProperties - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, }; private OptionsCollection UseBlockBodyForAccessors_ExpressionBodyForProperties_DisabledDiagnostic - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, ExpressionBodyPreference.Never, NotificationOption2.None }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, ExpressionBodyPreference.WhenPossible, NotificationOption2.None }, }; private OptionsCollection UseBlockBodyForAccessors_BlockBodyForProperties - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, }; private OptionsCollection UseBlockBodyForAccessors_BlockBodyForProperties_DisabledDiagnostic - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, ExpressionBodyPreference.Never, NotificationOption2.None }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, ExpressionBodyPreference.Never, NotificationOption2.None }, diff --git a/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs b/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs index b42a08165ac..40e4a00748d 100644 --- a/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs +++ b/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs @@ -23,49 +23,49 @@ protected override CodeRefactoringProvider CreateCodeRefactoringProvider(TestWor => new UseExpressionBodyCodeRefactoringProvider(); private OptionsCollection UseExpressionBodyForAccessors_BlockBodyForProperties - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, }; private OptionsCollection UseExpressionBodyForAccessors_BlockBodyForProperties_DisabledDiagnostic - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, ExpressionBodyPreference.WhenPossible, NotificationOption2.None }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, ExpressionBodyPreference.Never, NotificationOption2.None }, }; private OptionsCollection UseExpressionBodyForAccessors_ExpressionBodyForProperties - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, }; private OptionsCollection UseExpressionBodyForAccessors_ExpressionBodyForProperties_DisabledDiagnostic - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, ExpressionBodyPreference.WhenPossible, NotificationOption2.None }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, ExpressionBodyPreference.WhenPossible, NotificationOption2.None }, }; private OptionsCollection UseBlockBodyForAccessors_ExpressionBodyForProperties - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, }; private OptionsCollection UseBlockBodyForAccessors_BlockBodyForProperties - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, }; private OptionsCollection UseBlockBodyForAccessors_BlockBodyForProperties_DisabledDiagnostic - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, ExpressionBodyPreference.Never, NotificationOption2.None }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, ExpressionBodyPreference.Never, NotificationOption2.None }, diff --git a/src/roslyn/src/Features/Core/Portable/AddFileBanner/AbstractAddFileBannerCodeRefactoringProvider.cs b/src/roslyn/src/Features/Core/Portable/AddFileBanner/AbstractAddFileBannerCodeRefactoringProvider.cs index 5b811b6feb8..d2eaf81ad4d 100644 --- a/src/roslyn/src/Features/Core/Portable/AddFileBanner/AbstractAddFileBannerCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/AddFileBanner/AbstractAddFileBannerCodeRefactoringProvider.cs @@ -27,8 +27,8 @@ internal abstract class AbstractAddFileBannerCodeRefactoringProvider : SyntaxEdi protected abstract bool IsCommentStartCharacter(char ch); - protected sealed override ImmutableArray SupportedFixAllScopes { get; } - = [FixAllScope.Project, FixAllScope.Solution]; + protected sealed override ImmutableArray SupportedRefactorAllScopes { get; } + = [RefactorAllScope.Project, RefactorAllScope.Solution]; public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { @@ -145,7 +145,7 @@ private async Task> TryGetBannerAsync( return bannerService.GetFileBanner(token); } - protected sealed override async Task FixAllAsync( + protected sealed override async Task RefactorAllAsync( Document document, ImmutableArray fixAllSpans, SyntaxEditor editor, diff --git a/src/roslyn/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs b/src/roslyn/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs index 51df1ba2f1e..03876911bfb 100644 --- a/src/roslyn/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs +++ b/src/roslyn/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs @@ -143,7 +143,7 @@ private async Task> FindResultsAsync( // Caches so we don't produce the same data multiple times while searching // all over the solution. var project = document.Project; - var projectToAssembly = new ConcurrentDictionary>(concurrencyLevel: 2, capacity: project.Solution.ProjectIds.Count); + var projectToAssembly = new ConcurrentDictionary>(concurrencyLevel: 2, capacity: project.Solution.ProjectIds.Count); var referenceToCompilation = new ConcurrentDictionary(concurrencyLevel: 2, capacity: project.Solution.Projects.Sum(p => p.MetadataReferences.Count)); var finder = new SymbolReferenceFinder( @@ -168,7 +168,7 @@ private static bool IsHostOrRemoteWorkspace(Project project) => project.Solution.WorkspaceKind is WorkspaceKind.Host or WorkspaceKind.RemoteWorkspace; private async Task> FindResultsAsync( - ConcurrentDictionary> projectToAssembly, + ConcurrentDictionary> projectToAssembly, ConcurrentDictionary referenceToCompilation, Project project, int maxResults, @@ -213,7 +213,7 @@ private static async Task FindResultsInAllSymbolsInStartingProjectAsync( } private static async Task FindResultsInUnreferencedProjectSourceSymbolsAsync( - ConcurrentDictionary> projectToAssembly, + ConcurrentDictionary> projectToAssembly, Project project, ConcurrentQueue allSymbolReferences, int maxResults, SymbolReferenceFinder finder, bool exact, CancellationToken cancellationToken) { @@ -481,9 +481,7 @@ private static void AddRange(ConcurrentQueue allSymbolReferences, Imm protected static bool IsViableExtensionMethod(IMethodSymbol method, ITypeSymbol receiver) { if (receiver == null || method == null) - { return false; - } // It's possible that the 'method' we're looking at is from a different language than // the language we're currently in. For example, we might find the extension method @@ -496,9 +494,11 @@ protected static bool IsViableExtensionMethod(IMethodSymbol method, ITypeSymbol // to just always consider such methods viable. if (receiver.Language != method.Language) - { return false; - } + + // Uniformly check classic and modern extension methods. + if (method.IsClassicOrModernInstanceExtensionMethod(out var classicMethod)) + method = classicMethod; return method.ReduceExtensionMethod(receiver) != null; } diff --git a/src/roslyn/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs b/src/roslyn/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs index 01fc2d7372b..4f767ed93fe 100644 --- a/src/roslyn/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs +++ b/src/roslyn/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.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; using System.Collections.Concurrent; @@ -67,7 +65,7 @@ public SymbolReferenceFinder( _symbolSearchService = symbolSearchService; Options = options; _packageSources = packageSources; - _syntaxFacts = document.GetLanguageService(); + _syntaxFacts = document.GetRequiredLanguageService(); _namespacesInScope = GetNamespacesInScope(cancellationToken); _isWithinImport = owner.IsWithinImport(node); @@ -96,7 +94,7 @@ private INamespaceSymbol MapToCompilationNamespaceIfPossible(INamespaceSymbol co internal Task> FindInAllSymbolsInStartingProjectAsync(bool exact, CancellationToken cancellationToken) => DoAsync(new AllSymbolsProjectSearchScope(_owner, _document.Project, exact), cancellationToken); - internal Task> FindInSourceSymbolsInProjectAsync(ConcurrentDictionary> projectToAssembly, Project project, bool exact, CancellationToken cancellationToken) + internal Task> FindInSourceSymbolsInProjectAsync(ConcurrentDictionary> projectToAssembly, Project project, bool exact, CancellationToken cancellationToken) => DoAsync(new SourceSymbolsProjectSearchScope(_owner, projectToAssembly, project, exact), cancellationToken); internal Task> FindInMetadataSymbolsAsync(IAssemblySymbol assembly, Project assemblyProject, PortableExecutableReference metadataReference, bool exact, CancellationToken cancellationToken) @@ -165,7 +163,7 @@ private static void CalculateContext( syntaxFacts.GetNameAndArityOfSimpleName(nameNode, out name, out arity); inAttributeContext = syntaxFacts.IsNameOfAttribute(nameNode); - hasIncompleteParentMember = nameNode?.Parent?.RawKind == syntaxFacts.SyntaxKinds.IncompleteMember; + hasIncompleteParentMember = nameNode.GetRequiredParent().RawKind == syntaxFacts.SyntaxKinds.IncompleteMember; looksGeneric = syntaxFacts.LooksGeneric(nameNode); } @@ -296,12 +294,12 @@ private async Task> GetReferencesForMatchingFiel // We have code like "Color.Black". "Color" bound to a 'Color Color' property, and // 'Black' did not bind. We want to find a type called 'Color' that will actually // allow 'Black' to bind. - var syntaxFacts = _document.GetLanguageService(); + var syntaxFacts = _document.GetRequiredLanguageService(); if (syntaxFacts.IsNameOfSimpleMemberAccessExpression(nameNode) || syntaxFacts.IsNameOfMemberBindingExpression(nameNode)) { var expression = syntaxFacts.IsNameOfSimpleMemberAccessExpression(nameNode) - ? syntaxFacts.GetExpressionOfMemberAccessExpression(nameNode.Parent, allowImplicitTarget: true) + ? syntaxFacts.GetExpressionOfMemberAccessExpression(nameNode.GetRequiredParent(), allowImplicitTarget: true) : syntaxFacts.GetTargetOfMemberBinding(nameNode.Parent); if (expression is TSimpleNameSyntax simpleName) { @@ -370,7 +368,7 @@ private async Task> GetReferencesForMatchingExte var methodSymbols = OfType(symbols); var extensionMethodSymbols = GetViableExtensionMethods( - methodSymbols, nameNode.Parent, cancellationToken); + methodSymbols, nameNode.GetRequiredParent(), cancellationToken); var namespaceSymbols = extensionMethodSymbols.SelectAsArray(s => s.WithSymbol(s.Symbol.ContainingNamespace)); return GetNamespaceSymbolReferences(searchScope, namespaceSymbols); @@ -400,7 +398,7 @@ private ImmutableArray> GetViableExtensionMethodsWor ImmutableArray> methodSymbols) { return methodSymbols.WhereAsArray( - s => s.Symbol.IsExtensionMethod && + s => s.Symbol.IsClassicOrModernInstanceExtensionMethod() && s.Symbol.IsAccessibleWithin(_semanticModel.Compilation.Assembly)); } @@ -426,7 +424,7 @@ private async Task> GetReferencesForCollectionIn var methodSymbols = OfType(symbols).SelectAsArray(s => s.WithDesiredName(null)); var viableMethods = GetViableExtensionMethods( - methodSymbols, _node.Parent, cancellationToken); + methodSymbols, _node.GetRequiredParent(), cancellationToken); return GetNamespaceSymbolReferences(searchScope, viableMethods.SelectAsArray(m => m.WithSymbol(m.Symbol.ContainingNamespace))); @@ -562,7 +560,7 @@ private async Task> GetReferencesForDeconstructA } private async Task> GetReferencesForExtensionMethodAsync( - SearchScope searchScope, string name, ITypeSymbol type, Func predicate, CancellationToken cancellationToken) + SearchScope searchScope, string name, ITypeSymbol type, Func? predicate, CancellationToken cancellationToken) { var symbols = await searchScope.FindDeclarationsAsync( name, nameNode: null, filter: SymbolFilter.Member, cancellationToken).ConfigureAwait(false); diff --git a/src/roslyn/src/Features/Core/Portable/AddImport/SymbolResult.cs b/src/roslyn/src/Features/Core/Portable/AddImport/SymbolResult.cs index 09f95fc8091..a980bdf43ba 100644 --- a/src/roslyn/src/Features/Core/Portable/AddImport/SymbolResult.cs +++ b/src/roslyn/src/Features/Core/Portable/AddImport/SymbolResult.cs @@ -67,7 +67,7 @@ public bool DesiredNameMatchesSourceName(Document document) } } - private readonly struct SymbolResult(string desiredName, TSimpleNameSyntax nameNode, T symbol, double weight) where T : ISymbol + private readonly struct SymbolResult(string? desiredName, TSimpleNameSyntax nameNode, T symbol, double weight) where T : ISymbol { // The symbol that matched the string being searched for. public readonly T Symbol = symbol; @@ -77,7 +77,7 @@ private readonly struct SymbolResult(string desiredName, TSimpleNameSyntax na public readonly double Weight = weight; // The desired name to change the user text to if this was a fuzzy (spell-checking) match. - public readonly string DesiredName = desiredName; + public readonly string? DesiredName = desiredName; // The node to convert to the desired name public readonly TSimpleNameSyntax NameNode = nameNode; @@ -85,7 +85,7 @@ private readonly struct SymbolResult(string desiredName, TSimpleNameSyntax na public SymbolResult WithSymbol(T2 symbol) where T2 : ISymbol => new(DesiredName, NameNode, symbol, Weight); - internal SymbolResult WithDesiredName(string desiredName) + internal SymbolResult WithDesiredName(string? desiredName) => new(desiredName, NameNode, Symbol, Weight); } diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixes/CodeFixCollection.cs b/src/roslyn/src/Features/Core/Portable/CodeFixes/CodeFixCollection.cs index 34834da5aa1..a5d813b79a1 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixes/CodeFixCollection.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixes/CodeFixCollection.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; using System.Collections.Immutable; using Microsoft.CodeAnalysis.Text; @@ -17,7 +18,7 @@ internal sealed class CodeFixCollection( ImmutableArray fixes, FixAllState? fixAllState, ImmutableArray supportedScopes, - Diagnostic firstDiagnostic) + ImmutableArray diagnostics) { public object Provider { get; } = provider; public TextSpan TextSpan { get; } = span; @@ -28,5 +29,15 @@ internal sealed class CodeFixCollection( /// public FixAllState? FixAllState { get; } = fixAllState; public ImmutableArray SupportedScopes { get; } = supportedScopes.NullToEmpty(); - public Diagnostic FirstDiagnostic { get; } = firstDiagnostic; + + /// + /// Diagnostics this collection of fixes can fix. This is guaranteed to be non-empty. + /// + public ImmutableArray Diagnostics { get; } = ThrowIfDefaultOrEmpty(diagnostics); + + private static ImmutableArray ThrowIfDefaultOrEmpty(ImmutableArray diagnostics) + { + Contract.ThrowIfTrue(diagnostics.IsDefaultOrEmpty); + return diagnostics; + } } diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs b/src/roslyn/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs index 10c4eb96236..eb63f29395f 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs @@ -91,7 +91,7 @@ private static ImmutableArray GetConfigurations(Project project, IEnume ? new TopLevelConfigureCodeStyleOptionCodeAction(diagnostic, nestedActions.ToImmutable()) : nestedActions.Single(); - result.Add(new CodeFix(project, resultCodeAction, diagnostic)); + result.Add(new CodeFix(resultCodeAction, [diagnostic])); } } diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.cs b/src/roslyn/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.cs index 3ae7001b1ec..3a3d8850198 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.cs @@ -68,7 +68,7 @@ private static ImmutableArray GetConfigurations(Project project, IEnume } var codeAction = new TopLevelConfigureSeverityCodeAction(diagnostic, nestedActions.ToImmutableAndFree()); - result.Add(new CodeFix(project, codeAction, diagnostic)); + result.Add(new CodeFix(codeAction, [diagnostic])); // Bulk configuration is only supported for analyzer diagnostics. if (!SuppressionHelpers.IsCompilerDiagnostic(diagnostic)) @@ -111,7 +111,7 @@ void AddBulkConfigurationCodeFixes(ImmutableArray diagnostics, strin } var codeAction = new TopLevelBulkConfigureSeverityCodeAction(nestedActions.ToImmutableAndFree(), category); - result.Add(new CodeFix(project, codeAction, diagnostics)); + result.Add(new CodeFix(codeAction, diagnostics)); } } } diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/AbstractFixAllCodeFixCodeAction.cs b/src/roslyn/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/AbstractFixAllCodeFixCodeAction.cs deleted file mode 100644 index 1f087c2d009..00000000000 --- a/src/roslyn/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/AbstractFixAllCodeFixCodeAction.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.Linq; -using System.Reflection; -using System.Threading; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; - -namespace Microsoft.CodeAnalysis.CodeFixes; - -/// -/// Fix all code action for a code action registered by a . -/// -internal abstract class AbstractFixAllCodeFixCodeAction : AbstractFixAllCodeAction -{ - private static readonly HashSet s_predefinedCodeFixProviderNames = GetPredefinedCodeFixProviderNames(); - - protected AbstractFixAllCodeFixCodeAction( - IFixAllState fixAllState, bool showPreviewChangesDialog) - : base(fixAllState, showPreviewChangesDialog) - { - } - - protected sealed override IFixAllContext CreateFixAllContext(IFixAllState fixAllState, IProgress progressTracker, CancellationToken cancellationToken) - => new FixAllContext((FixAllState)fixAllState, progressTracker, cancellationToken); - - protected sealed override bool IsInternalProvider(IFixAllState fixAllState) - { - var exportAttributes = fixAllState.Provider.GetType().GetTypeInfo().GetCustomAttributes(typeof(ExportCodeFixProviderAttribute), false); - if (exportAttributes?.Any() == true) - { - var exportAttribute = (ExportCodeFixProviderAttribute)exportAttributes.First(); - return !string.IsNullOrEmpty(exportAttribute.Name) - && s_predefinedCodeFixProviderNames.Contains(exportAttribute.Name); - } - - return false; - } - - private static HashSet GetPredefinedCodeFixProviderNames() - { - var names = new HashSet(); - - var fields = typeof(PredefinedCodeFixProviderNames).GetTypeInfo().DeclaredFields; - foreach (var field in fields) - { - if (field.IsStatic) - { - names.Add((string)field.GetValue(null)!); - } - } - - return names; - } -} diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/FixMultipleCodeAction.cs b/src/roslyn/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/FixMultipleCodeAction.cs deleted file mode 100644 index 0f0822011ea..00000000000 --- a/src/roslyn/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/FixMultipleCodeAction.cs +++ /dev/null @@ -1,17 +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 Microsoft.CodeAnalysis.CodeFixesAndRefactorings; - -namespace Microsoft.CodeAnalysis.CodeFixes; - -internal sealed partial class FixMultipleCodeAction( - IFixAllState fixAllState, - string title, - string computingFixWaitDialogMessage) : AbstractFixAllCodeFixCodeAction(fixAllState, showPreviewChangesDialog: false) -{ - public override string Title => title; - - internal override string Message => computingFixWaitDialogMessage; -} diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/CodeFixService.FixAllDiagnosticProvider.cs b/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/CodeFixService.FixAllDiagnosticProvider.cs index 954b4091e23..b309f44ceb0 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/CodeFixService.FixAllDiagnosticProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/CodeFixService.FixAllDiagnosticProvider.cs @@ -44,20 +44,17 @@ public override async Task> GetDocumentDiagnosticsAsync( { var service = document.Project.Solution.Services.GetRequiredService(); var diagnostics = Filter(await service.GetDiagnosticsForIdsAsync( - document.Project, [document.Id], _diagnosticIds, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false)); + document.Project, [document.Id], _diagnosticIds, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false)); Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId != null)); return await diagnostics.ToDiagnosticsAsync(document.Project, cancellationToken).ConfigureAwait(false); } public override async Task> GetDocumentSpanDiagnosticsAsync(Document document, TextSpan fixAllSpan, CancellationToken cancellationToken) { - bool shouldIncludeDiagnostic(string id) => _diagnosticIds == null || _diagnosticIds.Contains(id); - var service = document.Project.Solution.Services.GetRequiredService(); var diagnostics = Filter(await service.GetDiagnosticsForSpanAsync( - document, fixAllSpan, shouldIncludeDiagnostic, - priorityProvider: new DefaultCodeActionRequestPriorityProvider(), - DiagnosticKind.All, cancellationToken).ConfigureAwait(false)); + document, fixAllSpan, DiagnosticIdFilter.Include(_diagnosticIds), + priority: null, DiagnosticKind.All, cancellationToken).ConfigureAwait(false)); Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId != null)); return await diagnostics.ToDiagnosticsAsync(document.Project, cancellationToken).ConfigureAwait(false); } @@ -67,7 +64,7 @@ public override async Task> GetAllDiagnosticsAsync(Proje // Get all diagnostics for the entire project, including document diagnostics. var service = project.Solution.Services.GetRequiredService(); var diagnostics = Filter(await service.GetDiagnosticsForIdsAsync( - project, documentIds: default, _diagnosticIds, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false)); + project, documentIds: default, _diagnosticIds, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false)); return await diagnostics.ToDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false); } @@ -76,7 +73,7 @@ public override async Task> GetProjectDiagnosticsAsync(P // Get all no-location diagnostics for the project, doesn't include document diagnostics. var service = project.Solution.Services.GetRequiredService(); var diagnostics = Filter(await service.GetProjectDiagnosticsForIdsAsync( - project, _diagnosticIds, shouldIncludeAnalyzer: null, cancellationToken).ConfigureAwait(false)); + project, _diagnosticIds, AnalyzerFilter.All, cancellationToken).ConfigureAwait(false)); Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId == null)); return await diagnostics.ToDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false); } diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/CodeFixService.cs b/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/CodeFixService.cs index c3ecf3a63bd..6504c01261e 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/CodeFixService.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/CodeFixService.cs @@ -70,9 +70,9 @@ public CodeFixService( _configurationProvidersMap = new(() => GetConfigurationProvidersPerLanguageMap(configurationProviders)); } - private Func? GetShouldIncludeDiagnosticPredicate( + private DiagnosticIdFilter GetShouldIncludeDiagnosticPredicate( TextDocument document, - ICodeActionRequestPriorityProvider priorityProvider) + CodeActionRequestPriority? priority) { // For Normal or Low priority, we only need to execute analyzers which can report at least one fixable // diagnostic that can have a non-suppression/configuration fix. @@ -80,35 +80,30 @@ public CodeFixService( // For CodeActionPriorityRequest.High, we only run compiler analyzer, which always has fixable diagnostics, // so we can return a null predicate here to include all diagnostics. - if (!(priorityProvider.Priority is CodeActionRequestPriority.Default or CodeActionRequestPriority.Low)) - return null; + if (!(priority is CodeActionRequestPriority.Default or CodeActionRequestPriority.Low)) + return DiagnosticIdFilter.All; - var hasWorkspaceFixers = TryGetWorkspaceFixersMap(document, out var workspaceFixersMap); + TryGetWorkspaceFixersMap(document, out var workspaceFixersMap); + workspaceFixersMap ??= ImmutableDictionary>.Empty; var projectFixersMap = GetProjectFixers(document); - return id => - { - if (hasWorkspaceFixers && workspaceFixersMap!.ContainsKey(id)) - return true; - - return projectFixersMap.ContainsKey(id); - }; + return DiagnosticIdFilter.Include([.. workspaceFixersMap.Keys, .. projectFixersMap.Keys]); } public async Task GetMostSevereFixAsync( - TextDocument document, TextSpan range, ICodeActionRequestPriorityProvider priorityProvider, CancellationToken cancellationToken) + TextDocument document, TextSpan range, CodeActionRequestPriority? priority, CancellationToken cancellationToken) { - using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.CodeFix_Summary, $"Pri{priorityProvider.Priority.GetPriorityInt()}.{nameof(GetMostSevereFixAsync)}"); + using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.CodeFix_Summary, $"Pri{priority.GetPriorityInt()}.{nameof(GetMostSevereFixAsync)}"); ImmutableArray allDiagnostics; using (TelemetryLogging.LogBlockTimeAggregatedHistogram( - FunctionId.CodeFix_Summary, $"Pri{priorityProvider.Priority.GetPriorityInt()}.{nameof(GetMostSevereFixAsync)}.{nameof(IDiagnosticAnalyzerService.GetDiagnosticsForSpanAsync)}")) + FunctionId.CodeFix_Summary, $"Pri{priority.GetPriorityInt()}.{nameof(GetMostSevereFixAsync)}.{nameof(IDiagnosticAnalyzerService.GetDiagnosticsForSpanAsync)}")) { var service = document.Project.Solution.Services.GetRequiredService(); allDiagnostics = await service.GetDiagnosticsForSpanAsync( - document, range, GetShouldIncludeDiagnosticPredicate(document, priorityProvider), - priorityProvider, DiagnosticKind.All, cancellationToken).ConfigureAwait(false); + document, range, GetShouldIncludeDiagnosticPredicate(document, priority), + priority, DiagnosticKind.All, cancellationToken).ConfigureAwait(false); // NOTE(cyrusn): We do not include suppressed diagnostics here as they are effectively hidden from the // user in the editor. As far as the user is concerned, there is no squiggle for it and no lightbulb @@ -116,7 +111,7 @@ public CodeFixService( allDiagnostics = allDiagnostics.WhereAsArray(d => !d.IsSuppressed); } - var copilotDiagnostics = await GetCopilotDiagnosticsAsync(document, range, priorityProvider.Priority, cancellationToken).ConfigureAwait(false); + var copilotDiagnostics = await GetCopilotDiagnosticsAsync(document, range, priority, cancellationToken).ConfigureAwait(false); allDiagnostics = allDiagnostics.AddRange(copilotDiagnostics); var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); @@ -160,7 +155,7 @@ public CodeFixService( await foreach (var collection in StreamFixesAsync( document, spanToDiagnostics, fixAllForInSpan: false, - priorityProvider, cancellationToken).ConfigureAwait(false)) + priority, cancellationToken).ConfigureAwait(false)) { // Stop at the result error we see. return collection; @@ -173,14 +168,14 @@ public CodeFixService( public async IAsyncEnumerable StreamFixesAsync( TextDocument document, TextSpan range, - ICodeActionRequestPriorityProvider priorityProvider, + CodeActionRequestPriority? priority, [EnumeratorCancellation] CancellationToken cancellationToken) { - using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.CodeFix_Summary, $"Pri{priorityProvider.Priority.GetPriorityInt()}"); + using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.CodeFix_Summary, $"Pri{priority.GetPriorityInt()}"); // We only need to compute suppression/configuration fixes when request priority is // 'CodeActionPriorityRequest.Lowest' or no priority was provided at all (so all providers should run). - var includeSuppressionFixes = priorityProvider.Priority is null or CodeActionRequestPriority.Lowest; + var includeSuppressionFixes = priority is null or CodeActionRequestPriority.Lowest; // REVIEW: this is the first and simplest design. basically, when ctrl+. is pressed, it asks diagnostic // service to give back current diagnostics for the given span, and it will use that to get fixes. @@ -195,17 +190,17 @@ public async IAsyncEnumerable StreamFixesAsync( ImmutableArray diagnostics; using (TelemetryLogging.LogBlockTimeAggregatedHistogram( - FunctionId.CodeFix_Summary, $"Pri{priorityProvider.Priority.GetPriorityInt()}.{nameof(IDiagnosticAnalyzerService.GetDiagnosticsForSpanAsync)}")) + FunctionId.CodeFix_Summary, $"Pri{priority.GetPriorityInt()}.{nameof(IDiagnosticAnalyzerService.GetDiagnosticsForSpanAsync)}")) { var service = document.Project.Solution.Services.GetRequiredService(); diagnostics = await service.GetDiagnosticsForSpanAsync( - document, range, GetShouldIncludeDiagnosticPredicate(document, priorityProvider), - priorityProvider, DiagnosticKind.All, cancellationToken).ConfigureAwait(false); + document, range, GetShouldIncludeDiagnosticPredicate(document, priority), + priority, DiagnosticKind.All, cancellationToken).ConfigureAwait(false); if (!includeSuppressionFixes) diagnostics = diagnostics.WhereAsArray(d => !d.IsSuppressed); } - var copilotDiagnostics = await GetCopilotDiagnosticsAsync(document, range, priorityProvider.Priority, cancellationToken).ConfigureAwait(false); + var copilotDiagnostics = await GetCopilotDiagnosticsAsync(document, range, priority, cancellationToken).ConfigureAwait(false); diagnostics = diagnostics.AddRange(copilotDiagnostics); if (diagnostics.IsEmpty) @@ -217,11 +212,11 @@ public async IAsyncEnumerable StreamFixesAsync( var spanToDiagnostics = ConvertToMap(text, diagnostics); // 'CodeActionRequestPriority.Lowest' is used when the client only wants suppression/configuration fixes. - if (priorityProvider.Priority != CodeActionRequestPriority.Lowest) + if (priority != CodeActionRequestPriority.Lowest) { await foreach (var collection in StreamFixesAsync( document, spanToDiagnostics, fixAllForInSpan: false, - priorityProvider, cancellationToken).ConfigureAwait(false)) + priority, cancellationToken).ConfigureAwait(false)) { yield return collection; } @@ -297,7 +292,7 @@ private static SortedDictionary> ConvertToMap( { var service = document.Project.Solution.Services.GetRequiredService(); diagnostics = await service.GetDiagnosticsForSpanAsync( - document, range, diagnosticId, priorityProvider: new DefaultCodeActionRequestPriorityProvider(), + document, range, diagnosticId, priority: null, DiagnosticKind.All, cancellationToken).ConfigureAwait(false); // NOTE(cyrusn): We do not include suppressed diagnostics here as they are effectively hidden from the @@ -317,7 +312,7 @@ private static SortedDictionary> ConvertToMap( }; await foreach (var collection in StreamFixesAsync( - document, spanToDiagnostics, fixAllForInSpan: true, new DefaultCodeActionRequestPriorityProvider(), + document, spanToDiagnostics, fixAllForInSpan: true, priority: null, cancellationToken).ConfigureAwait(false)) { if (collection.FixAllState is not null && collection.SupportedScopes.Contains(FixAllScope.Document)) @@ -443,7 +438,7 @@ private async IAsyncEnumerable StreamFixesAsync( TextDocument document, SortedDictionary> spanToDiagnostics, bool fixAllForInSpan, - ICodeActionRequestPriorityProvider priorityProvider, + CodeActionRequestPriority? priority, [EnumeratorCancellation] CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -500,13 +495,15 @@ private async IAsyncEnumerable StreamFixesAsync( if (TryGetWorkspaceFixersPriorityMap(document, out var fixersForLanguage)) allFixers = allFixers.Sort(new FixerComparer(allFixers, fixersForLanguage.Value)); - var extensionManager = document.Project.Solution.Services.GetService(); + var solution = document.Project.Solution; + var extensionManager = solution.Services.GetService(); + var diagnosticAnalyzerService = solution.Services.GetRequiredService(); // Run each CodeFixProvider to gather individual CodeFixes for reported diagnostics. // Ensure that no diagnostic has registered code actions from different code fix providers with same equivalance key. // This prevents duplicate registered code actions from NuGet and VSIX code fix providers. // See https://github.com/dotnet/roslyn/issues/18818 for details. - var uniqueDiagosticToEquivalenceKeysMap = new Dictionary>(); + var uniqueDiagnosticToEquivalenceKeysMap = new Dictionary>(); // NOTE: For backward compatibility, we allow multiple registered code actions from the same code fix provider // to have the same equivalence key. See https://github.com/dotnet/roslyn/issues/44553 for details. @@ -519,7 +516,7 @@ private async IAsyncEnumerable StreamFixesAsync( { cancellationToken.ThrowIfCancellationRequested(); - if (!priorityProvider.MatchesPriority(fixer)) + if (!await MatchesPriorityAsync(fixer).ConfigureAwait(false)) continue; foreach (var (span, diagnostics) in fixerToRangesAndDiagnostics[fixer]) @@ -553,13 +550,13 @@ private async IAsyncEnumerable StreamFixesAsync( { var primaryDiagnostic = dxs.First(); return GetCodeFixesAsync(document, primaryDiagnostic.Location.SourceSpan, fixer, fixerMetadata, - [primaryDiagnostic], uniqueDiagosticToEquivalenceKeysMap, + [primaryDiagnostic], uniqueDiagnosticToEquivalenceKeysMap, diagnosticAndEquivalenceKeyToFixersMap, cancellationToken); } else { return GetCodeFixesAsync(document, span, fixer, fixerMetadata, dxs, - uniqueDiagosticToEquivalenceKeysMap, diagnosticAndEquivalenceKeyToFixersMap, cancellationToken); + uniqueDiagnosticToEquivalenceKeysMap, diagnosticAndEquivalenceKeyToFixersMap, cancellationToken); } } }, @@ -578,7 +575,7 @@ private async IAsyncEnumerable StreamFixesAsync( } finally { - foreach (var pooledSet in uniqueDiagosticToEquivalenceKeysMap.Values) + foreach (var pooledSet in uniqueDiagnosticToEquivalenceKeysMap.Values) { pooledSet.Free(); } @@ -599,6 +596,41 @@ static void AddAllFixers( fixerToRangesAndDiagnostics.MultiAdd(fixer, (range, diagnostics)); } } + + // + // Returns true if the given should be considered a candidate when computing + // fixes for the given . + // + async Task MatchesPriorityAsync(CodeFixProvider codeFixProvider) + { + if (priority == null) + { + // We are computing fixes for all priorities + return true; + } + + if (priority == codeFixProvider.RequestPriority) + { + return true; + } + + if (priority == CodeActionRequestPriority.Low && + codeFixProvider.RequestPriority > CodeActionRequestPriority.Low && + await diagnosticAnalyzerService.IsAnyDiagnosticIdDeprioritizedAsync( + document.Project, codeFixProvider.FixableDiagnosticIds, cancellationToken).ConfigureAwait(false)) + { + // 'Low' priority can be used for two types of code fixers: + // 1. Those which explicitly set their 'RequestPriority' to 'Low' and + // 2. Those which can fix diagnostics for expensive analyzers which were de-prioritized + // to 'Low' priority bucket to improve lightbulb population performance. + // The first case is handled by the earlier check against matching priorities. For the second + // case, we accept fixers with any RequestPriority, as long as they can fix a diagnostic from + // an analyzer that was executed in the 'Low' bucket. + return true; + } + + return false; + } } private CodeChangeProviderMetadata? TryGetMetadata(CodeFixProvider fixer) @@ -650,7 +682,7 @@ private static async Task> GetCodeFixesAsync( // name metadata. action.AddCustomTagAndTelemetryInfo(fixerMetadata, fixer); - fixes.Add(new CodeFix(document.Project, action, applicableDiagnostics)); + fixes.Add(new CodeFix(action, applicableDiagnostics)); } } }, @@ -750,15 +782,14 @@ private async IAsyncEnumerable StreamConfigurationFixesAsync( { cancellationToken.ThrowIfCancellationRequested(); - var allDiagnostics = - await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity) - .ToDiagnosticsAsync(textDocument.Project, cancellationToken).ConfigureAwait(false); + var allDiagnostics = await diagnosticsWithSameSpan + .OrderByDescending(d => d.Severity) + .ToDiagnosticsAsync(textDocument.Project, cancellationToken).ConfigureAwait(false); var diagnostics = allDiagnostics.WhereAsArray(hasFix); - if (diagnostics.Length <= 0) - { - // this can happen for suppression case where all diagnostics can't be suppressed + + // this can happen for suppression case where all diagnostics can't be suppressed + if (diagnostics.IsEmpty) return null; - } var extensionManager = textDocument.Project.Solution.Services.GetRequiredService(); var fixes = await extensionManager.PerformFunctionAsync(fixer, @@ -776,13 +807,10 @@ await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity) var supportedScopes = ImmutableArray.Empty; if (fixAllProviderInfo != null && textDocument is Document document) { - var diagnosticIds = diagnostics.Where(fixAllProviderInfo.CanBeFixed) - .Select(d => d.Id) - .ToImmutableHashSet(); - - var diagnosticProvider = fixAllForInSpan - ? new FixAllPredefinedDiagnosticProvider(allDiagnostics) - : (FixAllContext.DiagnosticProvider)new FixAllDiagnosticProvider(diagnosticIds); + var diagnosticIds = diagnostics + .Where(fixAllProviderInfo.CanBeFixed) + .Select(d => d.Id) + .ToImmutableHashSet(); var codeFixProvider = (fixer as CodeFixProvider) ?? new WrapperCodeFixProvider((IConfigurationFixProvider)fixer, diagnostics.Select(d => d.Id)); @@ -795,14 +823,15 @@ await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity) FixAllScope.Document, fixes[0].Action.EquivalenceKey, diagnosticIds, - diagnosticProvider); + fixAllForInSpan + ? new FixAllPredefinedDiagnosticProvider(allDiagnostics) + : new FixAllDiagnosticProvider(diagnosticIds)); supportedScopes = fixAllProviderInfo.SupportedScopes; } return new CodeFixCollection( - fixer, fixesSpan, fixes, fixAllState, - supportedScopes, diagnostics.First()); + fixer, fixesSpan, fixes, fixAllState, supportedScopes, diagnostics); } /// Looks explicitly for an . diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/ICodeFixService.cs b/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/ICodeFixService.cs index 9c163339f49..61776b70b2c 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/ICodeFixService.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/ICodeFixService.cs @@ -14,14 +14,14 @@ namespace Microsoft.CodeAnalysis.CodeFixes; internal interface ICodeFixService { - IAsyncEnumerable StreamFixesAsync(TextDocument document, TextSpan textSpan, ICodeActionRequestPriorityProvider priorityProvider, CancellationToken cancellationToken); + IAsyncEnumerable StreamFixesAsync(TextDocument document, TextSpan textSpan, CodeActionRequestPriority? priority, CancellationToken cancellationToken); /// /// Similar to except that instead of streaming all results, this ends with the /// first. This will also attempt to return a fix for an error first, but will fall back to any fix if that /// does not succeed. /// - Task GetMostSevereFixAsync(TextDocument document, TextSpan range, ICodeActionRequestPriorityProvider priorityProvider, CancellationToken cancellationToken); + Task GetMostSevereFixAsync(TextDocument document, TextSpan range, CodeActionRequestPriority? priority, CancellationToken cancellationToken); Task GetDocumentFixAllForIdInSpanAsync(TextDocument document, TextSpan textSpan, string diagnosticId, DiagnosticSeverity severity, CancellationToken cancellationToken); Task ApplyCodeFixesForSpecificDiagnosticIdAsync(TDocument document, string diagnosticId, DiagnosticSeverity severity, IProgress progressTracker, CancellationToken cancellationToken) @@ -32,11 +32,11 @@ Task ApplyCodeFixesForSpecificDiagnosticIdAsync(TDocument internal static class ICodeFixServiceExtensions { public static IAsyncEnumerable StreamFixesAsync(this ICodeFixService service, TextDocument document, TextSpan range, CancellationToken cancellationToken) - => service.StreamFixesAsync(document, range, new DefaultCodeActionRequestPriorityProvider(), cancellationToken); + => service.StreamFixesAsync(document, range, priority: null, cancellationToken); public static Task> GetFixesAsync(this ICodeFixService service, TextDocument document, TextSpan range, CancellationToken cancellationToken) => service.StreamFixesAsync(document, range, cancellationToken).ToImmutableArrayAsync(cancellationToken); - public static Task> GetFixesAsync(this ICodeFixService service, TextDocument document, TextSpan textSpan, ICodeActionRequestPriorityProvider priorityProvider, CancellationToken cancellationToken) - => service.StreamFixesAsync(document, textSpan, priorityProvider, cancellationToken).ToImmutableArrayAsync(cancellationToken); + public static Task> GetFixesAsync(this ICodeFixService service, TextDocument document, TextSpan textSpan, CodeActionRequestPriority? priority, CancellationToken cancellationToken) + => service.StreamFixesAsync(document, textSpan, priority, cancellationToken).ToImmutableArrayAsync(cancellationToken); } diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs b/src/roslyn/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs index 964117f5ba1..e5e5b8521de 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs @@ -235,16 +235,14 @@ private async Task> GetSuppressionsAsync( { var codeAction = new TopLevelSuppressionCodeAction( diagnostic, nestedActions.ToImmutableAndClear()); - result.Add(new CodeFix(project, codeAction, diagnostic)); + result.Add(new CodeFix(codeAction, [diagnostic])); } } else if (!skipUnsuppress) { var codeAction = await RemoveSuppressionCodeAction.CreateAsync(suppressionTargetInfo, documentOpt, project, diagnostic, this, cancellationToken).ConfigureAwait(false); if (codeAction != null) - { - result.Add(new CodeFix(project, codeAction, diagnostic)); - } + result.Add(new CodeFix(codeAction, [diagnostic])); } } diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllCodeAction.cs b/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllCodeAction.cs index 7d712ec0dab..44cc9d1e5cc 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllCodeAction.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllCodeAction.cs @@ -3,11 +3,16 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; +using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; @@ -15,49 +20,61 @@ namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; /// Fix all code action for a code action registered by /// a or a . /// -internal abstract class AbstractFixAllCodeAction( - IFixAllState fixAllState, bool showPreviewChangesDialog) : CodeAction +internal sealed class RefactorOrFixAllCodeAction( + IRefactorOrFixAllState refactorOrFixAllState, + bool showPreviewChangesDialog, + string? title = null, + string? message = null) : CodeAction { + private static readonly ISet s_predefinedProviderNames = + typeof(PredefinedCodeFixProviderNames).GetTypeInfo().DeclaredFields.Concat(typeof(PredefinedCodeRefactoringProviderNames).GetTypeInfo().DeclaredFields) + .Where(field => field.IsStatic) + .Select(field => (string)field.GetValue(null)!) + .ToSet(); + private bool _showPreviewChangesDialog = showPreviewChangesDialog; - public IFixAllState FixAllState { get; } = fixAllState; + public IRefactorOrFixAllState RefactorOrFixAllState { get; } = refactorOrFixAllState; // We don't need to post process changes here as the inner code action created for Fix multiple code fix already executes. internal sealed override CodeActionCleanup Cleanup => CodeActionCleanup.None; /// - /// Determine if the is an internal first-party provider or not. + /// Creates a new with the given parameters. /// - protected abstract bool IsInternalProvider(IFixAllState fixAllState); - - /// - /// Creates a new with the given parameters. - /// - protected abstract IFixAllContext CreateFixAllContext(IFixAllState fixAllState, IProgress progressTracker, CancellationToken cancellationToken); + private static IRefactorOrFixAllContext CreateFixAllContext(IRefactorOrFixAllState state, IProgress progressTracker, CancellationToken cancellationToken) + { + return state switch + { + FixAllState fixAllState => new FixAllContext(fixAllState, progressTracker, cancellationToken), + RefactorAllState refactorAllState => new RefactorAllContext(refactorAllState, progressTracker, cancellationToken), + _ => throw ExceptionUtilities.UnexpectedValue(state), + }; + } public override string Title - => this.FixAllState.Scope switch + => title ?? (this.RefactorOrFixAllState.Scope switch { FixAllScope.Document => FeaturesResources.Document, FixAllScope.Project => FeaturesResources.Project, FixAllScope.Solution => FeaturesResources.Solution, FixAllScope.ContainingMember => FeaturesResources.Containing_Member, FixAllScope.ContainingType => FeaturesResources.Containing_Type, - _ => throw ExceptionUtilities.UnexpectedValue(this.FixAllState.Scope), - }; + _ => throw ExceptionUtilities.UnexpectedValue(this.RefactorOrFixAllState.Scope), + }); - internal override string Message => FeaturesResources.Computing_fix_all_occurrences_code_fix; + internal override string Message => message ?? FeaturesResources.Computing_fix_all_occurrences_code_fix; protected sealed override Task> ComputeOperationsAsync( IProgress progressTracker, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - FixAllLogger.LogState(FixAllState, IsInternalProvider(FixAllState)); + FixAllLogger.LogState(RefactorOrFixAllState, IsInternalProvider(RefactorOrFixAllState)); - var service = FixAllState.Project.Solution.Services.GetRequiredService(); + var service = RefactorOrFixAllState.Project.Solution.Services.GetRequiredService(); - var fixAllContext = CreateFixAllContext(FixAllState, progressTracker, cancellationToken); - progressTracker.Report(CodeAnalysisProgress.Description(fixAllContext.GetDefaultFixAllTitle())); + var fixAllContext = CreateFixAllContext(RefactorOrFixAllState, progressTracker, cancellationToken); + progressTracker.Report(CodeAnalysisProgress.Description(fixAllContext.GetDefaultTitle())); return service.GetFixAllOperationsAsync(fixAllContext, _showPreviewChangesDialog); } @@ -66,16 +83,38 @@ protected sealed override Task> ComputeOpera IProgress progressTracker, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - FixAllLogger.LogState(FixAllState, IsInternalProvider(FixAllState)); + FixAllLogger.LogState(RefactorOrFixAllState, IsInternalProvider(RefactorOrFixAllState)); - var service = FixAllState.Project.Solution.Services.GetRequiredService(); + var service = RefactorOrFixAllState.Project.Solution.Services.GetRequiredService(); - var fixAllContext = CreateFixAllContext(FixAllState, progressTracker, cancellationToken); - progressTracker.Report(CodeAnalysisProgress.Description(fixAllContext.GetDefaultFixAllTitle())); + var fixAllContext = CreateFixAllContext(RefactorOrFixAllState, progressTracker, cancellationToken); + progressTracker.Report(CodeAnalysisProgress.Description(fixAllContext.GetDefaultTitle())); return service.GetFixAllChangedSolutionAsync(fixAllContext); } + /// + /// Determine if the is an internal first-party provider or not. + /// + private static bool IsInternalProvider(IRefactorOrFixAllState fixAllState) + { + var exportAttributes = fixAllState.Provider.GetType().GetTypeInfo().GetCustomAttributes(typeof(ExportCodeFixProviderAttribute), false); + if (exportAttributes?.FirstOrDefault() is ExportCodeFixProviderAttribute codeFixAttribute) + { + return !string.IsNullOrEmpty(codeFixAttribute.Name) + && s_predefinedProviderNames.Contains(codeFixAttribute.Name); + } + + exportAttributes = fixAllState.Provider.GetType().GetTypeInfo().GetCustomAttributes(typeof(ExportCodeRefactoringProviderAttribute), false); + if (exportAttributes?.FirstOrDefault() is ExportCodeRefactoringProviderAttribute codeRefactoringAttribute) + { + return !string.IsNullOrEmpty(codeRefactoringAttribute.Name) + && s_predefinedProviderNames.Contains(codeRefactoringAttribute.Name); + } + + return false; + } + // internal for testing purposes. internal TestAccessor GetTestAccessor() => new(this); @@ -83,9 +122,9 @@ internal TestAccessor GetTestAccessor() // internal for testing purposes. internal readonly struct TestAccessor { - private readonly AbstractFixAllCodeAction _fixAllCodeAction; + private readonly RefactorOrFixAllCodeAction _fixAllCodeAction; - internal TestAccessor(AbstractFixAllCodeAction fixAllCodeAction) + internal TestAccessor(RefactorOrFixAllCodeAction fixAllCodeAction) => _fixAllCodeAction = fixAllCodeAction; /// diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllGetFixesService.cs b/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllGetFixesService.cs index c503e2c8be3..97b18de47ba 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllGetFixesService.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllGetFixesService.cs @@ -22,7 +22,7 @@ internal abstract class AbstractFixAllGetFixesService : IFixAllGetFixesService string fixAllTopLevelHeader, Glyph glyph); - public async Task GetFixAllChangedSolutionAsync(IFixAllContext fixAllContext) + public async Task GetFixAllChangedSolutionAsync(IRefactorOrFixAllContext fixAllContext) { var codeAction = await GetFixAllCodeActionAsync(fixAllContext).ConfigureAwait(false); if (codeAction == null) @@ -35,7 +35,7 @@ internal abstract class AbstractFixAllGetFixesService : IFixAllGetFixesService } public async Task> GetFixAllOperationsAsync( - IFixAllContext fixAllContext, bool showPreviewChangesDialog) + IRefactorOrFixAllContext fixAllContext, bool showPreviewChangesDialog) { var codeAction = await GetFixAllCodeActionAsync(fixAllContext).ConfigureAwait(false); if (codeAction == null) @@ -51,7 +51,7 @@ private async Task> GetFixAllOperationsAsync CodeAction codeAction, bool showPreviewChangesDialog, IProgress progressTracker, - IFixAllState fixAllState, + IRefactorOrFixAllState fixAllState, CancellationToken cancellationToken) { // We have computed the fix all occurrences code fix. @@ -153,7 +153,7 @@ private async Task> GetFixAllOperationsAsync } } - private static async Task GetFixAllCodeActionAsync(IFixAllContext fixAllContext) + private static async Task GetFixAllCodeActionAsync(IRefactorOrFixAllContext fixAllContext) { var fixAllKind = fixAllContext.State.FixAllKind; var functionId = fixAllKind switch @@ -175,7 +175,7 @@ private async Task> GetFixAllOperationsAsync CodeAction? action = null; try { - action = await fixAllContext.State.FixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false); + action = await fixAllContext.State.FixAllProvider.GetCodeActionAsync(fixAllContext).ConfigureAwait(false); } catch (OperationCanceledException) { diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/CodeActionRequestPriorityProvider.cs b/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/CodeActionRequestPriorityProvider.cs deleted file mode 100644 index cdb4e5550f8..00000000000 --- a/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/CodeActionRequestPriorityProvider.cs +++ /dev/null @@ -1,159 +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.Immutable; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace Microsoft.CodeAnalysis.CodeActions; - -internal interface ICodeActionRequestPriorityProvider -{ - /// - /// represents no specified priority. i.e. any priority should match this. - /// - CodeActionRequestPriority? Priority { get; } - - /// - /// Tracks the given as a de-prioritized analyzer that should be moved to - /// bucket. - /// - void AddDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer); - - bool IsDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer); - - /// - /// Indicates whether any deprioritized analyzer supports one of the passed in diagnostic ids. - /// - bool HasDeprioritizedAnalyzerSupportingDiagnosticId(ImmutableArray diagnosticIds); -} - -internal static class ICodeActionRequestPriorityProviderExtensions -{ - /// - /// Returns true if the given can report diagnostics that can have fixes from a code - /// fix provider with matching . This method is useful for performing a performance - /// optimization for lightbulb diagnostic computation, wherein we can reduce the set of analyzers to be executed - /// when computing fixes for a specific . - /// - public static bool MatchesPriority(this ICodeActionRequestPriorityProvider provider, DiagnosticAnalyzer analyzer) - { - var priority = provider.Priority; - - // If caller isn't asking for prioritized result, then run all analyzers. - if (priority is null) - return true; - - // 'CodeActionRequestPriority.Lowest' is used for suppression/configuration fixes, - // which requires all analyzer diagnostics. - if (priority == CodeActionRequestPriority.Lowest) - return true; - - // The compiler analyzer always counts for any priority. It's diagnostics may be fixed - // by high pri or normal pri fixers. - if (analyzer.IsCompilerAnalyzer()) - return true; - - // Check if we are computing diagnostics for 'CodeActionRequestPriority.Low' and - // this analyzer was de-prioritized to low priority bucket. - if (priority == CodeActionRequestPriority.Low && - provider.IsDeprioritizedAnalyzerWithLowPriority(analyzer)) - { - return true; - } - - // Now compute this analyzer's priority and compare it with the provider's request 'Priority'. - // Our internal 'IBuiltInAnalyzer' can specify custom request priority, while all - // the third-party analyzers are assigned 'Medium' priority. - var analyzerPriority = analyzer is IBuiltInAnalyzer { IsHighPriority: true } - ? CodeActionRequestPriority.High - : CodeActionRequestPriority.Default; - - return priority == analyzerPriority; - } - - /// - /// Returns true if the given should be considered a candidate when computing - /// fixes for the given . - /// - public static bool MatchesPriority(this ICodeActionRequestPriorityProvider provider, CodeFixProvider codeFixProvider) - { - if (provider.Priority == null) - { - // We are computing fixes for all priorities - return true; - } - - if (provider.Priority == codeFixProvider.RequestPriority) - { - return true; - } - - if (provider.Priority == CodeActionRequestPriority.Low - && provider.HasDeprioritizedAnalyzerSupportingDiagnosticId(codeFixProvider.FixableDiagnosticIds) - && codeFixProvider.RequestPriority > CodeActionRequestPriority.Low) - { - // 'Low' priority can be used for two types of code fixers: - // 1. Those which explicitly set their 'RequestPriority' to 'Low' and - // 2. Those which can fix diagnostics for expensive analyzers which were de-prioritized - // to 'Low' priority bucket to improve lightbulb population performance. - // The first case is handled by the earlier check against matching priorities. For the second - // case, we accept fixers with any RequestPriority, as long as they can fix a diagnostic from - // an analyzer that was executed in the 'Low' bucket. - return true; - } - - return false; - } -} - -internal sealed class DefaultCodeActionRequestPriorityProvider(CodeActionRequestPriority? priority = null) : ICodeActionRequestPriorityProvider -{ - private readonly object _gate = new(); - private HashSet? _lowPriorityAnalyzers; - private HashSet? _lowPriorityAnalyzerSupportedDiagnosticIds; - - public CodeActionRequestPriority? Priority { get; } = priority; - - public void AddDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer) - { - lock (_gate) - { - _lowPriorityAnalyzers ??= []; - _lowPriorityAnalyzerSupportedDiagnosticIds ??= []; - - _lowPriorityAnalyzers.Add(analyzer); - - foreach (var supportedDiagnostic in analyzer.SupportedDiagnostics) - _lowPriorityAnalyzerSupportedDiagnosticIds.Add(supportedDiagnostic.Id); - } - } - - public bool HasDeprioritizedAnalyzerSupportingDiagnosticId(ImmutableArray diagnosticIds) - { - lock (_gate) - { - if (_lowPriorityAnalyzerSupportedDiagnosticIds == null) - return false; - - foreach (var diagnosticId in diagnosticIds) - { - if (_lowPriorityAnalyzerSupportedDiagnosticIds.Contains(diagnosticId)) - return true; - } - - return false; - } - } - - public bool IsDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer) - { - lock (_gate) - { - return _lowPriorityAnalyzers != null && _lowPriorityAnalyzers.Contains(analyzer); - } - } -} diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/IFixAllGetFixesService.cs b/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/IFixAllGetFixesService.cs index 2f802c41053..da9494aa5bc 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/IFixAllGetFixesService.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/IFixAllGetFixesService.cs @@ -16,12 +16,12 @@ internal interface IFixAllGetFixesService : IWorkspaceService /// Computes the fix all occurrences code fix, brings up the preview changes dialog for the fix and /// returns the code action operations corresponding to the fix. /// - Task> GetFixAllOperationsAsync(IFixAllContext fixAllContext, bool showPreviewChangesDialog); + Task> GetFixAllOperationsAsync(IRefactorOrFixAllContext fixAllContext, bool showPreviewChangesDialog); /// /// Computes the fix all occurrences code fix and returns the changed solution. /// - Task GetFixAllChangedSolutionAsync(IFixAllContext fixAllContext); + Task GetFixAllChangedSolutionAsync(IRefactorOrFixAllContext fixAllContext); /// /// Previews the changes that would occur after a code fix and returns the updated solution with those changes. diff --git a/src/roslyn/src/Features/Core/Portable/CodeLens/CodeLensFindReferenceProgress.cs b/src/roslyn/src/Features/Core/Portable/CodeLens/CodeLensFindReferenceProgress.cs index 57d69469161..7ed457339fd 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeLens/CodeLensFindReferenceProgress.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeLens/CodeLensFindReferenceProgress.cs @@ -30,7 +30,7 @@ internal sealed class CodeLensFindReferencesProgress( private readonly CancellationTokenSource _aggregateCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); private readonly SyntaxNode _queriedNode = queriedNode; private readonly ISymbol _queriedSymbol = queriedDefinition; - private readonly ConcurrentSet _locations = new ConcurrentSet(LocationComparer.Instance); + private readonly ConcurrentSet _locations = new(LocationComparer.Instance); /// /// If the cap is 0, then there is no cap. diff --git a/src/roslyn/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs b/src/roslyn/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs index 07a2d4022da..0d6bff46bed 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs @@ -31,7 +31,7 @@ internal sealed class CodeRefactoringService( [ImportMany] IEnumerable> providers) : ICodeRefactoringService { private readonly Lazy>>> _lazyLanguageDocumentToProvidersMap = - new Lazy>>>(() => + new(() => ImmutableDictionary.CreateRange( DistributeLanguagesAndDocuments(providers) .GroupBy(lz => new ProviderKey(lz.Metadata.Language, lz.Metadata.DocumentKind, lz.Metadata.DocumentExtension)) diff --git a/src/roslyn/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs b/src/roslyn/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs deleted file mode 100644 index 9ce8a3ab1c2..00000000000 --- a/src/roslyn/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs +++ /dev/null @@ -1,21 +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.Threading; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; - -namespace Microsoft.CodeAnalysis.CodeRefactorings; - -/// -/// Fix all code action for a code action registered by a . -/// -internal sealed class FixAllCodeRefactoringCodeAction(IFixAllState fixAllState) : AbstractFixAllCodeAction(fixAllState, showPreviewChangesDialog: true) -{ - protected override IFixAllContext CreateFixAllContext(IFixAllState fixAllState, IProgress progressTracker, CancellationToken cancellationToken) - => new FixAllContext((FixAllState)fixAllState, progressTracker, cancellationToken); - - protected override bool IsInternalProvider(IFixAllState fixAllState) - => true; // FixAll for refactorings is currently only supported for internal code refactoring providers. -} diff --git a/src/roslyn/src/Features/Core/Portable/Completion/Providers/AbstractDocCommentCompletionProvider.cs b/src/roslyn/src/Features/Core/Portable/Completion/Providers/AbstractDocCommentCompletionProvider.cs index 4bf683611ab..3bed61c9165 100644 --- a/src/roslyn/src/Features/Core/Portable/Completion/Providers/AbstractDocCommentCompletionProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/Completion/Providers/AbstractDocCommentCompletionProvider.cs @@ -28,7 +28,7 @@ internal abstract class AbstractDocCommentCompletionProvider : LSPCompl private static readonly ImmutableArray s_topLevelSingleUseTagNames = [SummaryElementName, RemarksElementName, ExampleElementName, CompletionListElementName]; private static readonly Dictionary s_tagMap = - new Dictionary() + new() { // tagOpen textBeforeCaret $$ textAfterCaret tagClose { ExceptionElementName, ($"<{ExceptionElementName}", $" {CrefAttributeName}=\"", "\"", null) }, diff --git a/src/roslyn/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionCacheEntry.cs b/src/roslyn/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionCacheEntry.cs index 90708a1b727..7b0036ec537 100644 --- a/src/roslyn/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionCacheEntry.cs +++ b/src/roslyn/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionCacheEntry.cs @@ -35,7 +35,7 @@ public sealed class Builder(Checksum checksum, string langauge, IEqualityCompare private readonly Checksum _checksum = checksum; private readonly string _language = langauge; - private readonly MultiDictionary _mapBuilder = new MultiDictionary(comparer); + private readonly MultiDictionary _mapBuilder = new(comparer); public ExtensionMethodImportCompletionCacheEntry ToCacheEntry() { diff --git a/src/roslyn/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.cs b/src/roslyn/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.cs index 2723f8b73cf..0e7935145c2 100644 --- a/src/roslyn/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.cs @@ -29,7 +29,7 @@ internal abstract partial class AbstractConvertIfToSwitchCodeRefactoringProvider public abstract string GetTitle(bool forSwitchExpression); public abstract Analyzer CreateAnalyzer(ISyntaxFacts syntaxFacts, ParseOptions options); - protected sealed override ImmutableArray SupportedFixAllScopes => AllFixAllScopes; + protected sealed override ImmutableArray SupportedRefactorAllScopes => AllRefactorAllScopes; public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { @@ -178,7 +178,7 @@ static bool CanConvertSectionForSwitchExpression(bool supportsOrPattern, Analyze } } - protected sealed override async Task FixAllAsync( + protected sealed override async Task RefactorAllAsync( Document document, ImmutableArray fixAllSpans, SyntaxEditor editor, diff --git a/src/roslyn/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs b/src/roslyn/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs index f6dd0b180b2..fbbbad54f74 100644 --- a/src/roslyn/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs @@ -47,7 +47,7 @@ private readonly record struct InvocationData( protected abstract TExpressionSyntax ParseExpression(string text); - protected override ImmutableArray SupportedFixAllScopes { get; } = AllFixAllScopes; + protected override ImmutableArray SupportedRefactorAllScopes { get; } = AllRefactorAllScopes; private InvocationData? AnalyzeInvocation( Document document, @@ -251,7 +251,7 @@ bool ContainsIndex(string stringLiteralText, string indexString) } } - protected override async Task FixAllAsync( + protected override async Task RefactorAllAsync( Document document, ImmutableArray fixAllSpans, SyntaxEditor editor, diff --git a/src/roslyn/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs b/src/roslyn/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs index c506ff37ff1..ca2d258887a 100644 --- a/src/roslyn/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs +++ b/src/roslyn/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs @@ -284,7 +284,7 @@ await firstAction await foreach (var (codeFixCollection, success, applicationTime) in values.ConfigureAwait(false)) { - var diagnosticId = codeFixCollection.FirstDiagnostic.Id; + var diagnosticId = codeFixCollection.Diagnostics.First().Id; var providerName = GetProviderName(codeFixCollection); IncrementCount(diagnosticIdToCount, diagnosticId); @@ -350,9 +350,9 @@ static async (span, callback, args, cancellationToken) => if (codeFixCollection is { Provider: not IConfigurationFixProvider, - Fixes: [var codeFix, ..], + Fixes: [{ Diagnostics: [var primaryDiagnostic, ..] }, ..], } && - IsVisibleDiagnostic(codeFix.PrimaryDiagnostic.IsSuppressed, codeFix.PrimaryDiagnostic.Severity) && + IsVisibleDiagnostic(primaryDiagnostic.IsSuppressed, primaryDiagnostic.Severity) && (codeFixCollection.Provider.GetType().Namespace ?? "").StartsWith(RoslynPrefix)) { callback(codeFixCollection); diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs index bd1f7f9350b..3cf9965b222 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs @@ -16,103 +16,17 @@ namespace Microsoft.CodeAnalysis.Diagnostics; -internal static class CodeAnalysisDiagnosticAnalyzerServiceHelpers -{ - private static Func GetDiagnosticAnalyzerFilter( - Project project, DiagnosticAnalyzerInfoCache infoCache) - { - return analyzer => - { - if (analyzer == FileContentLoadAnalyzer.Instance || - analyzer == GeneratorDiagnosticsPlaceholderAnalyzer.Instance || - analyzer.IsCompilerAnalyzer()) - { - return true; - } - - if (analyzer.IsBuiltInAnalyzer()) - { - // always return true for builtin analyzer. we can't use - // descriptor check since many builtin analyzer always return - // hidden descriptor regardless what descriptor it actually - // return on runtime. they do this so that they can control - // severity through option page rather than rule set editor. - // this is special behavior only ide analyzer can do. we hope - // once we support editorconfig fully, third party can use this - // ability as well and we can remove this kind special treatment on builtin - // analyzer. - return true; - } - - if (analyzer is DiagnosticSuppressor) - { - // Always execute diagnostic suppressors. - return true; - } - - if (project.CompilationOptions is null) - { - // Skip compilation options based checks for non-C#/VB projects. - return true; - } - - // For most of analyzers, the number of diagnostic descriptors is small, so this should be cheap. - var descriptors = infoCache.GetDiagnosticDescriptors(analyzer); - var analyzerConfigOptions = project.GetAnalyzerConfigOptions(); - return descriptors.Any(static (d, arg) => - { - var severity = d.GetEffectiveSeverity( - arg.CompilationOptions, - arg.analyzerConfigOptions?.ConfigOptionsWithFallback, - arg.analyzerConfigOptions?.TreeOptions); - return severity != ReportDiagnostic.Hidden; - }, - (project.CompilationOptions, analyzerConfigOptions)); - }; - } - - public static async Task> ForceCodeAnalysisDiagnosticsAsync( - IDiagnosticAnalyzerService diagnosticAnalyzerService, Project project, DiagnosticAnalyzerInfoCache infoCache, CancellationToken cancellationToken) - { - // We are being asked to explicitly analyze this project. As such we do *not* want to use the - // default rules determining which analyzers to run. For example, even if compiler diagnostics - // are set to 'none' for live diagnostics, we still want to run them here. - // - // As such, we are very intentionally not calling into _diagnosticAnalyzerService.GetDefaultAnalyzerFilter - // here. We want to control the rules entirely when this is called. - var filter = GetDiagnosticAnalyzerFilter(project, infoCache); - - // Compute all the diagnostics for all the documents in the project. - // - // Note: in this case we want diagnostics for source generated documents as well. So ensure those are - // generated and included in the results. - var sourceGeneratorDocuments = await project.GetSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false); - - var documentDiagnostics = await diagnosticAnalyzerService.GetDiagnosticsForIdsAsync( - project, [.. project.DocumentIds, .. project.AdditionalDocumentIds, .. sourceGeneratorDocuments.Select(d => d.Id)], - diagnosticIds: null, filter, includeLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false); - - // Then all the non-document diagnostics for that project as well. - var projectDiagnostics = await diagnosticAnalyzerService.GetProjectDiagnosticsForIdsAsync( - project, diagnosticIds: null, filter, cancellationToken).ConfigureAwait(false); - - return [.. documentDiagnostics, .. projectDiagnostics]; - } -} - [ExportWorkspaceServiceFactory(typeof(ICodeAnalysisDiagnosticAnalyzerService)), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class CodeAnalysisDiagnosticAnalyzerServiceFactory( - DiagnosticAnalyzerInfoCache.SharedGlobalCache infoCache) : IWorkspaceServiceFactory +internal sealed class CodeAnalysisDiagnosticAnalyzerServiceFactory() : IWorkspaceServiceFactory { public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) - => new CodeAnalysisDiagnosticAnalyzerService(infoCache.AnalyzerInfoCache, workspaceServices.Workspace); + => new CodeAnalysisDiagnosticAnalyzerService(workspaceServices.Workspace); private sealed class CodeAnalysisDiagnosticAnalyzerService : ICodeAnalysisDiagnosticAnalyzerService { private readonly IDiagnosticAnalyzerService _diagnosticAnalyzerService; - private readonly DiagnosticAnalyzerInfoCache _infoCache; private readonly Workspace _workspace; /// @@ -131,12 +45,9 @@ private sealed class CodeAnalysisDiagnosticAnalyzerService : ICodeAnalysisDiagno /// private readonly ConcurrentSet _clearedProjectIds = []; - public CodeAnalysisDiagnosticAnalyzerService( - DiagnosticAnalyzerInfoCache infoCache, - Workspace workspace) + public CodeAnalysisDiagnosticAnalyzerService(Workspace workspace) { _workspace = workspace; - _infoCache = infoCache; _diagnosticAnalyzerService = _workspace.Services.GetRequiredService(); _ = workspace.RegisterWorkspaceChangedHandler(OnWorkspaceChanged); @@ -178,8 +89,8 @@ public async ValueTask RunAnalysisAsync(Project project, CancellationToken cance Contract.ThrowIfFalse(project.Solution.Workspace == _workspace); - var diagnostics = await CodeAnalysisDiagnosticAnalyzerServiceHelpers.ForceCodeAnalysisDiagnosticsAsync( - _diagnosticAnalyzerService, project, _infoCache, cancellationToken).ConfigureAwait(false); + var diagnostics = await _diagnosticAnalyzerService.ForceRunCodeAnalysisDiagnosticsAsync( + project, cancellationToken).ConfigureAwait(false); // Add the given project to the analyzed projects list **after** analysis has completed. // We need this ordering to ensure that 'HasProjectBeenAnalyzed' call above functions correctly. diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs index a00eb7c65a5..769656aaa43 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs @@ -5,9 +5,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.IO; -using System.Linq; -using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics.Telemetry; diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs index 580e6b6a701..295df004e97 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs @@ -2,13 +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.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Diagnostics; @@ -24,25 +22,24 @@ internal interface IDiagnosticAnalyzerService : IWorkspaceService void RequestDiagnosticRefresh(); /// - /// The default analyzer filter that will be used in functions like if - /// no filter is provided. The default filter has the following rules: - /// - /// The standard compiler analyzer will not be run if the compiler diagnostic scope is . - /// A regular analyzer will not be run if is false. - /// A regular analyzer will not be run if if the background analysis scope is . - /// If a set of diagnostic ids are provided, the analyzer will not be run unless it declares at least one - /// descriptor in that set. - /// Otherwise, the analyzer will be run - /// + /// Forces analyzers to run on the given project and return all diagnostics, regardless of current environment + /// settings (like 'only run analyzers on open files', etc.). This is meant to be used by explicit invocations + /// of features like "Run Code Analysis". Note: not all analyzers will necessarily run. For example, analyzers + /// where all diagnostic descriptors are currently hidden will not run, as they would not produce any actual + /// diagnostics. /// - /// An additional filter that can accept or reject analyzers that the default - /// rules have accepted. - Func GetDefaultAnalyzerFilter( - Project project, ImmutableHashSet? diagnosticIds, Func? additionalFilter = null); + Task> ForceRunCodeAnalysisDiagnosticsAsync( + Project project, CancellationToken cancellationToken); - /// - Task> GetDeprioritizationCandidatesAsync( - Project project, ImmutableArray analyzers, CancellationToken cancellationToken); + /// + /// Returns if any of the given diagnostic IDs belong to an analyzer that is considered + /// 'deprioritized'. Deprioritized analyzers are ones that are considered expensive, due to registering symbol-end + /// and semantic-model actions. Because of their high cost, we want to avoid running them in the priority case, and only run them in the case. + /// + Task IsAnyDiagnosticIdDeprioritizedAsync( + Project project, ImmutableArray diagnosticIds, CancellationToken cancellationToken); /// /// Gets document diagnostics of the given diagnostic ids and/or analyzers from the given project. @@ -55,10 +52,7 @@ Task> GetDeprioritizationCandidatesAsync( /// Optional documents to scope the returned diagnostics. If , /// then diagnostics will be returned for and . /// Optional set of diagnostic IDs to scope the returned diagnostics. - /// Optional callback to filter out analyzers to execute for computing diagnostics. - /// If not present, will be used. If present, no default behavior - /// is used, and the callback is defered to entirely. To augment the existing default rules call - /// explicitly, and pass the result of that into this method. + /// Which analyzers to run. /// /// Indicates if local document diagnostics must be returned. /// Local diagnostics are the ones that are reported by analyzers on the same file for which the callback was received @@ -70,7 +64,7 @@ Task> GetDeprioritizationCandidatesAsync( /// project must be analyzed to get the complete set of non-local document diagnostics. /// Task> GetDiagnosticsForIdsAsync( - Project project, ImmutableArray documentIds, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, CancellationToken cancellationToken); + Project project, ImmutableArray documentIds, ImmutableHashSet? diagnosticIds, AnalyzerFilter analyzerFilter, bool includeLocalDocumentDiagnostics, CancellationToken cancellationToken); /// /// Get project diagnostics (diagnostics with no source location) of the given diagnostic ids and/or analyzers from @@ -80,42 +74,39 @@ Task> GetDiagnosticsForIdsAsync( /// /// Project to fetch the diagnostics for. /// Optional set of diagnostic IDs to scope the returned diagnostics. - /// Optional callback to filter out analyzers to execute for computing diagnostics. - /// If not present, will be used. If present, no default behavior - /// is used, and the callback is defered to entirely. To augment the existing default rules call - /// explicitly, and pass the result of that into this method. + /// Which analyzers to run. Task> GetProjectDiagnosticsForIdsAsync( - Project project, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, CancellationToken cancellationToken); + Project project, ImmutableHashSet? diagnosticIds, AnalyzerFilter analyzerFilter, CancellationToken cancellationToken); /// /// Return up to date diagnostics for the given span for the document /// - /// This can be expensive since it is force analyzing diagnostics if it doesn't have up-to-date one yet. - /// Predicate filters out analyzers from execution if - /// none of its reported diagnostics should be included in the result. - /// /// Non-local diagnostics for the requested document are not returned. In other words, only the diagnostics /// produced by running the requested filtered set of analyzers only on this document are returned here. /// To get non-local diagnostics for a document, use . Non-local diagnostics /// will always be returned for the document in that case. /// Task> GetDiagnosticsForSpanAsync( - TextDocument document, TextSpan? range, Func? shouldIncludeDiagnostic, - ICodeActionRequestPriorityProvider priorityProvider, + TextDocument document, TextSpan? range, + DiagnosticIdFilter diagnosticIdFilter, + CodeActionRequestPriority? priority, DiagnosticKind diagnosticKind, CancellationToken cancellationToken); + /// + Task>> GetDiagnosticDescriptorsPerReferenceAsync( + Solution solution, ProjectId? projectId, CancellationToken cancellationToken); + /// A project within where can be found Task> GetDiagnosticDescriptorsAsync( Solution solution, ProjectId projectId, AnalyzerReference analyzerReference, string language, CancellationToken cancellationToken); - /// - Task>> GetDiagnosticDescriptorsPerReferenceAsync( + /// + /// For all analyzers in the given solution, return the descriptor ids of all compilation end diagnostics. + /// Note: this does not include the "built in compiler analyzer". + /// + Task> GetCompilationEndDiagnosticDescriptorIdsAsync( Solution solution, CancellationToken cancellationToken); - - /// - Task>> GetDiagnosticDescriptorsPerReferenceAsync( - Project project, CancellationToken cancellationToken); } internal static class IDiagnosticAnalyzerServiceExtensions @@ -127,12 +118,12 @@ internal static class IDiagnosticAnalyzerServiceExtensions /// This can be expensive since it is force analyzing diagnostics if it doesn't have up-to-date one yet. /// /// - public static Task> GetDiagnosticsForSpanAsync(this IDiagnosticAnalyzerService service, - TextDocument document, TextSpan? range, DiagnosticKind diagnosticKind, CancellationToken cancellationToken) + public static Task> GetDiagnosticsForSpanAsync( + this IDiagnosticAnalyzerService service, TextDocument document, TextSpan? range, DiagnosticKind diagnosticKind, CancellationToken cancellationToken) => service.GetDiagnosticsForSpanAsync( document, range, diagnosticId: null, - priorityProvider: new DefaultCodeActionRequestPriorityProvider(), + priority: null, diagnosticKind, cancellationToken); @@ -146,12 +137,23 @@ public static Task> GetDiagnosticsForSpanAsync(th /// public static Task> GetDiagnosticsForSpanAsync(this IDiagnosticAnalyzerService service, TextDocument document, TextSpan? range, string? diagnosticId, - ICodeActionRequestPriorityProvider priorityProvider, + CodeActionRequestPriority? priority, DiagnosticKind diagnosticKind, CancellationToken cancellationToken) { - Func? shouldIncludeDiagnostic = diagnosticId != null ? id => id == diagnosticId : null; - return service.GetDiagnosticsForSpanAsync(document, range, shouldIncludeDiagnostic, - priorityProvider, diagnosticKind, cancellationToken); + var filter = diagnosticId != null + ? DiagnosticIdFilter.Include([diagnosticId]) + : DiagnosticIdFilter.All; + return service.GetDiagnosticsForSpanAsync( + document, range, filter, priority, diagnosticKind, cancellationToken); } + + public static Task>> GetDiagnosticDescriptorsPerReferenceAsync( + this IDiagnosticAnalyzerService service, Solution solution, CancellationToken cancellationToken) + => service.GetDiagnosticDescriptorsPerReferenceAsync(solution, projectId: null, cancellationToken); + + public static Task>> GetDiagnosticDescriptorsPerReferenceAsync( + this IDiagnosticAnalyzerService service, Project project, CancellationToken cancellationToken) + => service.GetDiagnosticDescriptorsPerReferenceAsync(project.Solution, project.Id, cancellationToken); + } diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Options/DiagnosticOptionsStorage.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Options/DiagnosticOptionsStorage.cs index f43204d5ab3..2ff08e3318d 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Options/DiagnosticOptionsStorage.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Options/DiagnosticOptionsStorage.cs @@ -10,7 +10,4 @@ internal sealed class DiagnosticOptionsStorage { public static readonly Option2 LogTelemetryForBackgroundAnalyzerExecution = new( "dotnet_log_telemetry_for_background_analyzer_execution", defaultValue: false); - - public static readonly Option2 LightbulbSkipExecutingDeprioritizedAnalyzers = new( - "dotnet_lightbulb_skip_executing_deprioritized_analyzers", defaultValue: false); } diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.HostAnalyzerInfo.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.HostAnalyzerInfo.cs index 58aa721c7ba..0b94a695210 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.HostAnalyzerInfo.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.HostAnalyzerInfo.cs @@ -74,7 +74,7 @@ private static int GetPriority(DiagnosticAnalyzer state) /// /// Return s for the given . /// - public ImmutableArray GetProjectAnalyzers(Project project) + private ImmutableArray GetProjectAnalyzers_OnlyCallInProcess(Project project) { if (!s_projectToAnalyzers.TryGetValue(project, out var lazyAnalyzers)) { @@ -94,15 +94,15 @@ public ImmutableArray GetProjectAnalyzers(Project project) ImmutableArray ComputeProjectAnalyzers() { - var hostAnalyzerInfo = GetOrCreateHostAnalyzerInfo(project); - var projectAnalyzerInfo = GetOrCreateProjectAnalyzerInfo(project); + var hostAnalyzerInfo = GetOrCreateHostAnalyzerInfo_OnlyCallInProcess(project); + var projectAnalyzerInfo = GetOrCreateProjectAnalyzerInfo_OnlyCallInProcess(project); return hostAnalyzerInfo.OrderedAllAnalyzers.AddRange(projectAnalyzerInfo.Analyzers); } } - private HostAnalyzerInfo GetOrCreateHostAnalyzerInfo(Project project) + private HostAnalyzerInfo GetOrCreateHostAnalyzerInfo_OnlyCallInProcess(Project project) { - var projectAnalyzerInfo = GetOrCreateProjectAnalyzerInfo(project); + var projectAnalyzerInfo = GetOrCreateProjectAnalyzerInfo_OnlyCallInProcess(project); var solution = project.Solution; var key = new HostAnalyzerInfoKey(project.Language, project.State.HasSdkCodeStyleAnalyzers, solution.SolutionState.Analyzers.HostAnalyzerReferences); diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.ProjectStates.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.ProjectStates.cs index 3ed6b47e911..c3e86428b5d 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.ProjectStates.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.ProjectStates.cs @@ -37,7 +37,7 @@ internal ProjectAnalyzerInfo( } } - private ProjectAnalyzerInfo GetOrCreateProjectAnalyzerInfo(Project project) + private ProjectAnalyzerInfo GetOrCreateProjectAnalyzerInfo_OnlyCallInProcess(Project project) { return ImmutableInterlocked.GetOrAdd( ref _projectAnalyzerStateMap, diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.cs index 7b667debb71..a55f09302ea 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; @@ -25,7 +26,6 @@ namespace Microsoft.CodeAnalysis.Diagnostics; internal sealed class DiagnosticAnalyzerServiceFactory( IGlobalOptionService globalOptions, IDiagnosticsRefresher diagnosticsRefresher, - DiagnosticAnalyzerInfoCache.SharedGlobalCache globalCache, [Import(AllowDefault = true)] IAsynchronousOperationListenerProvider? listenerProvider) : IWorkspaceServiceFactory { public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) @@ -33,7 +33,6 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) return new DiagnosticAnalyzerService( globalOptions, diagnosticsRefresher, - globalCache, listenerProvider, workspaceServices.Workspace); } @@ -57,7 +56,7 @@ internal sealed partial class DiagnosticAnalyzerService private readonly IGlobalOptionService _globalOptions; private readonly IDiagnosticsRefresher _diagnosticsRefresher; - private readonly DiagnosticAnalyzerInfoCache _analyzerInfoCache; + private readonly DiagnosticAnalyzerInfoCache _analyzerInfoCache = new(); private readonly DiagnosticAnalyzerTelemetry _telemetry = new(); private readonly IncrementalMemberEditAnalyzer _incrementalMemberEditAnalyzer = new(); @@ -76,11 +75,9 @@ internal sealed partial class DiagnosticAnalyzerService public DiagnosticAnalyzerService( IGlobalOptionService globalOptions, IDiagnosticsRefresher diagnosticsRefresher, - DiagnosticAnalyzerInfoCache.SharedGlobalCache globalCache, IAsynchronousOperationListenerProvider? listenerProvider, Workspace workspace) { - _analyzerInfoCache = globalCache.AnalyzerInfoCache; _listener = listenerProvider?.GetListener(FeatureAttribute.DiagnosticService) ?? AsynchronousOperationListenerProvider.NullListener; _globalOptions = globalOptions; _diagnosticsRefresher = diagnosticsRefresher; @@ -119,77 +116,46 @@ public static bool IsGlobalOptionAffectingDiagnostics(IOption2 option) public void RequestDiagnosticRefresh() => _diagnosticsRefresher.RequestWorkspaceRefresh(); - private ImmutableArray GetDiagnosticAnalyzers( - Project project, - ImmutableHashSet? diagnosticIds, - Func? shouldIncludeAnalyzer) + public TestAccessor GetTestAccessor() + => new(this); + + public readonly struct TestAccessor(DiagnosticAnalyzerService service) { - shouldIncludeAnalyzer ??= GetDefaultAnalyzerFilter(project, diagnosticIds, additionalFilter: null); + public ImmutableArray GetAnalyzers(Project project) + => service.GetProjectAnalyzers_OnlyCallInProcess(project); - var analyzersForProject = GetProjectAnalyzers(project); - return analyzersForProject.WhereAsArray(shouldIncludeAnalyzer); - } + public Task> AnalyzeProjectInProcessAsync( + Project project, CompilationWithAnalyzersPair compilationWithAnalyzers, bool logPerformanceInfo, bool getTelemetryInfo, CancellationToken cancellationToken) + => service.AnalyzeInProcessAsync(documentAnalysisScope: null, project, compilationWithAnalyzers, logPerformanceInfo, getTelemetryInfo, cancellationToken); - public Func GetDefaultAnalyzerFilter( - Project project, ImmutableHashSet? diagnosticIds, Func? additionalFilter) - => analyzer => + public async Task> GetDeprioritizedAnalyzersAsync(Project project) { - if (!DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(analyzer, project, this._globalOptions)) - return false; + using var _ = ArrayBuilder.GetInstance(out var builder); - if (additionalFilter != null && !additionalFilter(analyzer)) - return false; - - if (diagnosticIds != null && _analyzerInfoCache.GetDiagnosticDescriptors(analyzer).All(d => !diagnosticIds.Contains(d.Id))) - return false; + foreach (var analyzer in service.GetProjectAnalyzers_OnlyCallInProcess(project)) + { + if (await service.IsDeprioritizedAnalyzerAsync(project, analyzer, CancellationToken.None).ConfigureAwait(false)) + builder.Add(analyzer); + } - return true; - }; + return builder.ToImmutableAndClear(); + } - public Task> GetDiagnosticsForIdsAsync( - Project project, ImmutableArray documentIds, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, CancellationToken cancellationToken) - { - var analyzers = GetDiagnosticAnalyzers(project, diagnosticIds, shouldIncludeAnalyzer); - - return ProduceProjectDiagnosticsAsync( - project, analyzers, diagnosticIds, - // Ensure we compute and return diagnostics for both the normal docs and the additional docs in this - // project if no specific document id was requested. - documentIds.IsDefault ? [.. project.DocumentIds, .. project.AdditionalDocumentIds] : documentIds, - includeLocalDocumentDiagnostics, - includeNonLocalDocumentDiagnostics: true, - // return diagnostics specific to one project or document - includeProjectNonLocalResult: documentIds.IsDefault, - cancellationToken); - } + public async Task> GetDeprioritizedDiagnosticIdsAsync(Project project) + { + var builder = ImmutableHashSet.CreateBuilder(); - public Task> GetProjectDiagnosticsForIdsAsync( - Project project, - ImmutableHashSet? diagnosticIds, - Func? shouldIncludeAnalyzer, - CancellationToken cancellationToken) - { - var analyzers = GetDiagnosticAnalyzers(project, diagnosticIds, shouldIncludeAnalyzer); - - return ProduceProjectDiagnosticsAsync( - project, analyzers, diagnosticIds, - documentIds: [], - includeLocalDocumentDiagnostics: false, - includeNonLocalDocumentDiagnostics: false, - includeProjectNonLocalResult: true, - cancellationToken); - } + await service.PopulateDeprioritizedDiagnosticIdMapAsync(project, CancellationToken.None).ConfigureAwait(false); - public TestAccessor GetTestAccessor() - => new(this); + foreach (var analyzer in service.GetProjectAnalyzers_OnlyCallInProcess(project)) + { + Contract.ThrowIfFalse(DiagnosticAnalyzerService.s_analyzerToDeprioritizedDiagnosticIds.TryGetValue(analyzer, out var set)); + if (set != null) + builder.UnionWith(set); + } - public readonly struct TestAccessor(DiagnosticAnalyzerService service) - { - public ImmutableArray GetAnalyzers(Project project) - => service.GetProjectAnalyzers(project); + return builder.ToImmutableHashSet(); - public Task> AnalyzeProjectInProcessAsync( - Project project, CompilationWithAnalyzersPair compilationWithAnalyzers, bool logPerformanceInfo, bool getTelemetryInfo, CancellationToken cancellationToken) - => service.AnalyzeInProcessAsync(documentAnalysisScope: null, project, compilationWithAnalyzers, logPerformanceInfo, getTelemetryInfo, cancellationToken); + } } } diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_CompilationWithAnalyzersPair.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_CompilationWithAnalyzersPair.cs index 67710bdd738..47329a52495 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_CompilationWithAnalyzersPair.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_CompilationWithAnalyzersPair.cs @@ -40,7 +40,7 @@ private static readonly ConditionalWeakTable< /// private static readonly SemaphoreSlim s_gate = new(initialCount: 1); - private static async Task GetOrCreateCompilationWithAnalyzersAsync( + private static async Task GetOrCreateCompilationWithAnalyzers_OnlyCallInProcessAsync( Project project, ImmutableArray analyzers, HostAnalyzerInfo hostAnalyzerInfo, diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_DeprioritizationCandidates.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_DeprioritizationCandidates.cs index 3327c42192a..435cbbf6b66 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_DeprioritizationCandidates.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_DeprioritizationCandidates.cs @@ -2,7 +2,9 @@ // 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.Linq; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -21,46 +23,67 @@ internal sealed partial class DiagnosticAnalyzerService /// We accept that this cache may be inaccurate in such scenarios as they are likely rare, and this only /// serves as a simple heuristic to order analyzer execution. If wrong, it's not a major deal. /// - private static readonly ConditionalWeakTable> s_analyzerToIsDeprioritizationCandidateMap = new(); + private static readonly ConditionalWeakTable?> s_analyzerToDeprioritizedDiagnosticIds = new(); - private async Task> GetDeprioritizationCandidatesInProcessAsync( - Project project, ImmutableArray analyzers, CancellationToken cancellationToken) + private async Task IsDeprioritizedAnalyzerAsync( + Project project, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) { - using var _ = ArrayBuilder.GetInstance(out var builder); + await PopulateDeprioritizedDiagnosticIdMapAsync(project, cancellationToken).ConfigureAwait(false); - HostAnalyzerInfo? hostAnalyzerInfo = null; + // this can't fail as the above call populates the CWT entries for all analyzers within that project if missing. + Contract.ThrowIfFalse(s_analyzerToDeprioritizedDiagnosticIds.TryGetValue(analyzer, out var set)); + + return set != null; + } + + private async ValueTask PopulateDeprioritizedDiagnosticIdMapAsync(Project project, CancellationToken cancellationToken) + { + await IsAnyDiagnosticIdDeprioritizedAsync(project, diagnosticIds: [], cancellationToken).ConfigureAwait(false); + } + + public async Task IsAnyDeprioritizedDiagnosticIdInProcessAsync( + Project project, ImmutableArray diagnosticIds, CancellationToken cancellationToken) + { CompilationWithAnalyzersPair? compilationWithAnalyzers = null; + var analyzers = GetProjectAnalyzers_OnlyCallInProcess(project); foreach (var analyzer in analyzers) { - if (!s_analyzerToIsDeprioritizationCandidateMap.TryGetValue(analyzer, out var boxedBool)) + if (!s_analyzerToDeprioritizedDiagnosticIds.TryGetValue(analyzer, out var deprioritizedIds)) { - if (hostAnalyzerInfo is null) + if (compilationWithAnalyzers is null) { - hostAnalyzerInfo = GetOrCreateHostAnalyzerInfo(project); - compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzersAsync( - project, analyzers, hostAnalyzerInfo, this.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); + compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzers_OnlyCallInProcessAsync( + project, analyzers, GetOrCreateHostAnalyzerInfo_OnlyCallInProcess(project), this.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); } - boxedBool = new(await IsCandidateForDeprioritizationBasedOnRegisteredActionsAsync(analyzer).ConfigureAwait(false)); + deprioritizedIds = await ComputeDeprioritizedDiagnosticIdsAsync(analyzer).ConfigureAwait(false); + #if NET - s_analyzerToIsDeprioritizationCandidateMap.TryAdd(analyzer, boxedBool); + s_analyzerToDeprioritizedDiagnosticIds.TryAdd(analyzer, deprioritizedIds); #else - lock (s_analyzerToIsDeprioritizationCandidateMap) + lock (s_analyzerToDeprioritizedDiagnosticIds) { - if (!s_analyzerToIsDeprioritizationCandidateMap.TryGetValue(analyzer, out var existing)) - s_analyzerToIsDeprioritizationCandidateMap.Add(analyzer, boxedBool); + if (!s_analyzerToDeprioritizedDiagnosticIds.TryGetValue(analyzer, out var existing)) + s_analyzerToDeprioritizedDiagnosticIds.Add(analyzer, deprioritizedIds); } #endif + } - if (boxedBool.Value) - builder.Add(analyzer); + if (deprioritizedIds != null) + { + foreach (var id in diagnosticIds) + { + if (deprioritizedIds.Contains(id)) + return true; + } + } } - return builder.ToImmutableAndClear(); + return false; - async Task IsCandidateForDeprioritizationBasedOnRegisteredActionsAsync(DiagnosticAnalyzer analyzer) + async ValueTask?> ComputeDeprioritizedDiagnosticIdsAsync(DiagnosticAnalyzer analyzer) { // We deprioritize SymbolStart/End and SemanticModel analyzers from 'Normal' to 'Low' priority bucket, // as these are computationally more expensive. @@ -69,14 +92,17 @@ async Task IsCandidateForDeprioritizationBasedOnRegisteredActionsAsync(Dia analyzer.IsWorkspaceDiagnosticAnalyzer() || analyzer.IsCompilerAnalyzer()) { - return false; + return null; } var telemetryInfo = await compilationWithAnalyzers.GetAnalyzerTelemetryInfoAsync(analyzer, cancellationToken).ConfigureAwait(false); if (telemetryInfo == null) - return false; + return null; + + if (telemetryInfo.SymbolStartActionsCount == 0 && telemetryInfo.SemanticModelActionsCount == 0) + return null; - return telemetryInfo.SymbolStartActionsCount > 0 || telemetryInfo.SemanticModelActionsCount > 0; + return [.. analyzer.SupportedDiagnostics.Select(d => d.Id)]; } } } diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_ForceCodeAnalysisDiagnostics.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_ForceCodeAnalysisDiagnostics.cs new file mode 100644 index 00000000000..1f42875aea4 --- /dev/null +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_ForceCodeAnalysisDiagnostics.cs @@ -0,0 +1,102 @@ +// 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.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Shared.Extensions; + +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal sealed partial class DiagnosticAnalyzerService +{ + public async ValueTask> ForceRunCodeAnalysisDiagnosticsInProcessAsync( + Project project, CancellationToken cancellationToken) + { + // We are being asked to explicitly analyze this project. As such we do *not* want to use the + // default rules determining which analyzers to run. For example, even if compiler diagnostics + // are set to 'none' for live diagnostics, we still want to run them here. + // + // As such, we are very intentionally not calling into this.GetDefaultAnalyzerFilter + // here. We want to control the rules entirely when this is called. + var analyzers = GetProjectAnalyzers_OnlyCallInProcess(project); + var filteredAnalyzers = analyzers.WhereAsArray(ShouldIncludeAnalyzer); + + // Compute document and project diagnostics in parallel. + + // Compute all the diagnostics for all the documents in the project. + var documentDiagnosticsTask = GetDiagnosticsForIdsAsync(); + + // Then all the non-document diagnostics for that project as well. + var projectDiagnosticsTask = this.GetProjectDiagnosticsForIdsInProcessAsync( + project, diagnosticIds: null, filteredAnalyzers, cancellationToken); + + await Task.WhenAll(documentDiagnosticsTask, projectDiagnosticsTask).ConfigureAwait(false); + + return [.. await documentDiagnosticsTask.ConfigureAwait(false), .. await projectDiagnosticsTask.ConfigureAwait(false)]; + + async Task> GetDiagnosticsForIdsAsync() + { + + // Note: in this case we want diagnostics for source generated documents as well. So ensure those are + // generated and included in the results. + var sourceGeneratorDocuments = await project.GetSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false); + + return await this.GetDiagnosticsForIdsInProcessAsync( + project, [.. project.DocumentIds, .. project.AdditionalDocumentIds, .. sourceGeneratorDocuments.Select(d => d.Id)], + diagnosticIds: null, filteredAnalyzers, includeLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false); + } + + bool ShouldIncludeAnalyzer(DiagnosticAnalyzer analyzer) + { + if (analyzer == FileContentLoadAnalyzer.Instance || + analyzer == GeneratorDiagnosticsPlaceholderAnalyzer.Instance || + analyzer.IsCompilerAnalyzer()) + { + return true; + } + + if (analyzer.IsBuiltInAnalyzer()) + { + // always return true for builtin analyzer. we can't use + // descriptor check since many builtin analyzer always return + // hidden descriptor regardless what descriptor it actually + // return on runtime. they do this so that they can control + // severity through option page rather than rule set editor. + // this is special behavior only ide analyzer can do. we hope + // once we support editorconfig fully, third party can use this + // ability as well and we can remove this kind special treatment on builtin + // analyzer. + return true; + } + + if (analyzer is DiagnosticSuppressor) + { + // Always execute diagnostic suppressors. + return true; + } + + if (project.CompilationOptions is null) + { + // Skip compilation options based checks for non-C#/VB projects. + return true; + } + + // For most of analyzers, the number of diagnostic descriptors is small, so this should be cheap. + var descriptors = this._analyzerInfoCache.GetDiagnosticDescriptors(analyzer); + var analyzerConfigOptions = project.GetAnalyzerConfigOptions(); + return descriptors.Any(static (d, arg) => + { + var severity = d.GetEffectiveSeverity( + arg.CompilationOptions, + arg.analyzerConfigOptions?.ConfigOptionsWithFallback, + arg.analyzerConfigOptions?.TreeOptions); + return severity != ReportDiagnostic.Hidden; + }, + (project.CompilationOptions, analyzerConfigOptions)); + } + } +} diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_GetDiagnosticsForSpan.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_GetDiagnosticsForSpan.cs index f7e96ec7181..cf3b9e151b5 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_GetDiagnosticsForSpan.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_GetDiagnosticsForSpan.cs @@ -16,7 +16,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics; @@ -37,26 +36,18 @@ private static async Task> GetDiagnosticsForSpanAsync( + public async Task> GetDiagnosticsForSpanInProcessAsync( TextDocument document, TextSpan? range, - Func? shouldIncludeDiagnostic, - ICodeActionRequestPriorityProvider? priorityProvider, + DiagnosticIdFilter diagnosticIdFilter, + CodeActionRequestPriority? priority, DiagnosticKind diagnosticKind, CancellationToken cancellationToken) { - // Note: due to the in-memory work that priorityProvider and shouldIncludeDiagnostic need to do, - // much of this function runs locally (in process) to determine which analyzers to run, before - // finally making a call out to OOP to actually do the work. - - // always make sure that analyzer is called on background thread. - await Task.Yield().ConfigureAwait(false); - priorityProvider ??= new DefaultCodeActionRequestPriorityProvider(); - var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); var project = document.Project; - var unfilteredAnalyzers = GetProjectAnalyzers(project); + var unfilteredAnalyzers = GetProjectAnalyzers_OnlyCallInProcess(project); var analyzers = unfilteredAnalyzers .WhereAsArray(a => DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(a, project, _globalOptions)); @@ -72,25 +63,21 @@ public async Task> GetDiagnosticsForSpanAsync( // member edit analysis. This analysis is currently only enabled with LSP pull diagnostics. var incrementalAnalysis = range is null && document is Document { SupportsSyntaxTree: true }; - using var _1 = PooledHashSet.GetInstance(out var deprioritizationCandidates); - - deprioritizationCandidates.AddRange(await this.GetDeprioritizationCandidatesAsync( - project, analyzers, cancellationToken).ConfigureAwait(false)); - - var (syntaxAnalyzers, semanticSpanAnalyzers, semanticDocumentAnalyzers) = GetAllAnalyzers(); - syntaxAnalyzers = FilterAnalyzers(syntaxAnalyzers, AnalysisKind.Syntax, range, deprioritizationCandidates); - semanticSpanAnalyzers = FilterAnalyzers(semanticSpanAnalyzers, AnalysisKind.Semantic, range, deprioritizationCandidates); - semanticDocumentAnalyzers = FilterAnalyzers(semanticDocumentAnalyzers, AnalysisKind.Semantic, span: null, deprioritizationCandidates); + var (syntaxAnalyzers, semanticSpanAnalyzers, semanticDocumentAnalyzers) = await GetAllAnalyzersAsync().ConfigureAwait(false); + syntaxAnalyzers = await FilterAnalyzersAsync(syntaxAnalyzers, AnalysisKind.Syntax, range).ConfigureAwait(false); + semanticSpanAnalyzers = await FilterAnalyzersAsync(semanticSpanAnalyzers, AnalysisKind.Semantic, range).ConfigureAwait(false); + semanticDocumentAnalyzers = await FilterAnalyzersAsync(semanticDocumentAnalyzers, AnalysisKind.Semantic, span: null).ConfigureAwait(false); - var allDiagnostics = await this.ComputeDiagnosticsAsync( + var allDiagnostics = await this.ComputeDiagnosticsInProcessAsync( document, range, analyzers, syntaxAnalyzers, semanticSpanAnalyzers, semanticDocumentAnalyzers, incrementalAnalysis, logPerformanceInfo, cancellationToken).ConfigureAwait(false); return allDiagnostics.WhereAsArray(ShouldInclude); - (ImmutableArray syntaxAnalyzers, - ImmutableArray semanticSpanAnalyzers, - ImmutableArray semanticDocumentAnalyzers) GetAllAnalyzers() + async ValueTask<( + ImmutableArray syntaxAnalyzers, + ImmutableArray semanticSpanAnalyzers, + ImmutableArray semanticDocumentAnalyzers)> GetAllAnalyzersAsync() { try { @@ -104,11 +91,11 @@ public async Task> GetDiagnosticsForSpanAsync( using var _2 = ArrayBuilder.GetInstance(out var semanticSpanBasedAnalyzers); using var _3 = ArrayBuilder.GetInstance(out var semanticDocumentBasedAnalyzers); - using var _4 = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.RequestDiagnostics_Summary, $"Pri{priorityProvider.Priority.GetPriorityInt()}"); + using var _4 = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.RequestDiagnostics_Summary, $"Pri{priority.GetPriorityInt()}"); foreach (var analyzer in analyzers) { - if (!ShouldIncludeAnalyzer(analyzer, shouldIncludeDiagnostic, priorityProvider, this)) + if (!await ShouldIncludeAnalyzerAsync(analyzer).ConfigureAwait(false)) continue; bool includeSyntax = true, includeSemantic = true; @@ -166,15 +153,10 @@ public async Task> GetDiagnosticsForSpanAsync( } } - // Local functions - static bool ShouldIncludeAnalyzer( - DiagnosticAnalyzer analyzer, - Func? shouldIncludeDiagnostic, - ICodeActionRequestPriorityProvider priorityProvider, - DiagnosticAnalyzerService owner) + async ValueTask ShouldIncludeAnalyzerAsync(DiagnosticAnalyzer analyzer) { // Skip executing analyzer if its priority does not match the request priority. - if (!priorityProvider.MatchesPriority(analyzer)) + if (!await MatchesPriorityAsync(analyzer).ConfigureAwait(false)) return false; // Special case DocumentDiagnosticAnalyzer to never skip these document analyzers based on @@ -185,31 +167,69 @@ static bool ShouldIncludeAnalyzer( return true; // Skip analyzer if none of its reported diagnostics should be included. - if (shouldIncludeDiagnostic != null && - !owner._analyzerInfoCache.GetDiagnosticDescriptors(analyzer).Any(static (a, shouldIncludeDiagnostic) => shouldIncludeDiagnostic(a.Id), shouldIncludeDiagnostic)) + if (diagnosticIdFilter != DiagnosticIdFilter.All) { - return false; + var descriptors = _analyzerInfoCache.GetDiagnosticDescriptors(analyzer); + return diagnosticIdFilter.Allow(descriptors.Select(d => d.Id)); } return true; } - ImmutableArray FilterAnalyzers( + // + // Returns true if the given can report diagnostics that can have fixes from a code + // fix provider with matching . This method is useful for performing a performance + // optimization for lightbulb diagnostic computation, wherein we can reduce the set of analyzers to be executed + // when computing fixes for a specific . + // + async Task MatchesPriorityAsync(DiagnosticAnalyzer analyzer) + { + // If caller isn't asking for prioritized result, then run all analyzers. + if (priority is null) + return true; + + // 'CodeActionRequestPriority.Lowest' is used for suppression/configuration fixes, + // which requires all analyzer diagnostics. + if (priority == CodeActionRequestPriority.Lowest) + return true; + + // The compiler analyzer always counts for any priority. It's diagnostics may be fixed + // by high pri or normal pri fixers. + if (analyzer.IsCompilerAnalyzer()) + return true; + + // Check if we are computing diagnostics for 'CodeActionRequestPriority.Low' and + // this analyzer was de-prioritized to low priority bucket. + if (priority == CodeActionRequestPriority.Low && + await this.IsDeprioritizedAnalyzerAsync(project, analyzer, cancellationToken).ConfigureAwait(false)) + { + return true; + } + + // Now compute this analyzer's priority and compare it with the provider's request 'Priority'. + // Our internal 'IBuiltInAnalyzer' can specify custom request priority, while all + // the third-party analyzers are assigned 'Medium' priority. + var analyzerPriority = analyzer is IBuiltInAnalyzer { IsHighPriority: true } + ? CodeActionRequestPriority.High + : CodeActionRequestPriority.Default; + + return priority == analyzerPriority; + } + + async Task> FilterAnalyzersAsync( ImmutableArray analyzers, AnalysisKind kind, - TextSpan? span, - HashSet deprioritizationCandidates) + TextSpan? span) { using var _1 = ArrayBuilder.GetInstance(analyzers.Length, out var filteredAnalyzers); foreach (var analyzer in analyzers) { - Debug.Assert(priorityProvider.MatchesPriority(analyzer)); - // Check if this is an expensive analyzer that needs to be de-prioritized to a lower priority bucket. // If so, we skip this analyzer from execution in the current priority bucket. // We will subsequently execute this analyzer in the lower priority bucket. - if (TryDeprioritizeAnalyzer(analyzer, kind, span, deprioritizationCandidates)) + if (await ShouldDeprioritizeAnalyzerAsync(analyzer, kind, span).ConfigureAwait(false)) continue; filteredAnalyzers.Add(analyzer); @@ -218,9 +238,8 @@ ImmutableArray FilterAnalyzers( return filteredAnalyzers.ToImmutableAndClear(); } - bool TryDeprioritizeAnalyzer( - DiagnosticAnalyzer analyzer, AnalysisKind kind, TextSpan? span, - HashSet deprioritizationCandidates) + async ValueTask ShouldDeprioritizeAnalyzerAsync( + DiagnosticAnalyzer analyzer, AnalysisKind kind, TextSpan? span) { // PERF: In order to improve lightbulb performance, we perform de-prioritization optimization for certain analyzers // that moves the analyzer to a lower priority bucket. However, to ensure that de-prioritization happens for very rare cases, @@ -229,44 +248,33 @@ bool TryDeprioritizeAnalyzer( // 2. We are processing 'CodeActionRequestPriority.Normal' priority request. // 3. Analyzer registers certain actions that are known to lead to high performance impact due to its broad analysis scope, // such as SymbolStart/End actions and SemanticModel actions. - // 4. Analyzer did not report a diagnostic on the same line in prior document snapshot. // Conditions 1. and 2. if (kind != AnalysisKind.Semantic || !span.HasValue || - priorityProvider.Priority != CodeActionRequestPriority.Default) + priority != CodeActionRequestPriority.Default) { return false; } - Debug.Assert(span.Value.Length < text.Length); - // Condition 3. // Check if this is a candidate analyzer that can be de-prioritized into a lower priority bucket based on registered actions. - if (!deprioritizationCandidates.Contains(analyzer)) - return false; - - // 'LightbulbSkipExecutingDeprioritizedAnalyzers' option determines if we want to execute this analyzer - // in low priority bucket or skip it completely. If the option is not set, track the de-prioritized - // analyzer to be executed in low priority bucket. - // Note that 'AddDeprioritizedAnalyzerWithLowPriority' call below mutates the state in the provider to - // track this analyzer. This ensures that when the owner of this provider calls us back to execute - // the low priority bucket, we can still get back to this analyzer and execute it that time. - if (!this._globalOptions.GetOption(DiagnosticOptionsStorage.LightbulbSkipExecutingDeprioritizedAnalyzers)) - priorityProvider.AddDeprioritizedAnalyzerWithLowPriority(analyzer); - - return true; + return await this.IsDeprioritizedAnalyzerAsync(project, analyzer, cancellationToken).ConfigureAwait(false); } bool ShouldInclude(DiagnosticData diagnostic) { - return diagnostic.DocumentId == document.Id && - (range == null || range.Value.IntersectsWith(diagnostic.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text))) - && (shouldIncludeDiagnostic == null || shouldIncludeDiagnostic(diagnostic.Id)); + if (diagnostic.DocumentId != document.Id) + return false; + + if (range != null && !range.Value.IntersectsWith(diagnostic.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text))) + return false; + + return diagnosticIdFilter.Allow(diagnostic.Id); } } - public async Task> ComputeDiagnosticsInProcessAsync( + private async Task> ComputeDiagnosticsInProcessAsync( TextDocument document, TextSpan? range, ImmutableArray allAnalyzers, @@ -280,8 +288,8 @@ public async Task> ComputeDiagnosticsInProcessAsy // We log performance info when we are computing diagnostics for a span var project = document.Project; - var hostAnalyzerInfo = GetOrCreateHostAnalyzerInfo(project); - var compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzersAsync( + var hostAnalyzerInfo = GetOrCreateHostAnalyzerInfo_OnlyCallInProcess(project); + var compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzers_OnlyCallInProcessAsync( document.Project, allAnalyzers, hostAnalyzerInfo, this.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); using var _ = ArrayBuilder.GetInstance(out var list); diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_ProduceProjectDiagnostics.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_ProduceProjectDiagnostics.cs index 267a4678e6e..0aec147ab79 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_ProduceProjectDiagnostics.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_ProduceProjectDiagnostics.cs @@ -16,11 +16,107 @@ namespace Microsoft.CodeAnalysis.Diagnostics; internal sealed partial class DiagnosticAnalyzerService { - public async Task> ProduceProjectDiagnosticsInProcessAsync( + /// + /// Should only be called from other "InProcess" methods as this loads and realizes the DiagnosticAnalyzers. + /// + private ImmutableArray GetDiagnosticAnalyzersInProcess( + Project project, + ImmutableHashSet? diagnosticIds, + AnalyzerFilter analyzerFilter) + { + var analyzersForProject = GetProjectAnalyzers_OnlyCallInProcess(project); + return analyzersForProject.WhereAsArray(ShouldIncludeAnalyzer); + + bool ShouldIncludeAnalyzer(DiagnosticAnalyzer analyzer) + { + if (analyzer.IsCompilerAnalyzer()) + { + if ((analyzerFilter & AnalyzerFilter.CompilerAnalyzer) == 0) + return false; + } + else + { + if ((analyzerFilter & AnalyzerFilter.NonCompilerAnalyzer) == 0) + return false; + } + + if (!DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(analyzer, project, this._globalOptions)) + return false; + + if (diagnosticIds != null && _analyzerInfoCache.GetDiagnosticDescriptors(analyzer).All(d => !diagnosticIds.Contains(d.Id))) + return false; + + return true; + } + } + + private Task> GetDiagnosticsForIdsInProcessAsync( Project project, + ImmutableArray documentIds, + ImmutableHashSet? diagnosticIds, + AnalyzerFilter analyzerFilter, + bool includeLocalDocumentDiagnostics, + CancellationToken cancellationToken) + { + return GetDiagnosticsForIdsInProcessAsync( + project, documentIds, diagnosticIds, + GetDiagnosticAnalyzersInProcess(project, diagnosticIds, analyzerFilter), + includeLocalDocumentDiagnostics, cancellationToken); + } + + private Task> GetDiagnosticsForIdsInProcessAsync( + Project project, + ImmutableArray documentIds, + ImmutableHashSet? diagnosticIds, ImmutableArray analyzers, + bool includeLocalDocumentDiagnostics, + CancellationToken cancellationToken) + { + return ProduceProjectDiagnosticsInProcessAsync( + project, diagnosticIds, + // Ensure we compute and return diagnostics for both the normal docs and the additional docs in this + // project if no specific document id was requested. + documentIds.IsDefault ? [.. project.DocumentIds, .. project.AdditionalDocumentIds] : documentIds, + analyzers, + includeLocalDocumentDiagnostics, + includeNonLocalDocumentDiagnostics: true, + // return diagnostics specific to one project or document + includeProjectNonLocalResult: documentIds.IsDefault, + cancellationToken); + } + + private Task> GetProjectDiagnosticsForIdsInProcessAsync( + Project project, + ImmutableHashSet? diagnosticIds, + AnalyzerFilter analyzerFilter, + CancellationToken cancellationToken) + { + return GetProjectDiagnosticsForIdsInProcessAsync( + project, diagnosticIds, + GetDiagnosticAnalyzersInProcess(project, diagnosticIds, analyzerFilter), + cancellationToken); + } + + private Task> GetProjectDiagnosticsForIdsInProcessAsync( + Project project, + ImmutableHashSet? diagnosticIds, + ImmutableArray analyzers, + CancellationToken cancellationToken) + { + return ProduceProjectDiagnosticsInProcessAsync( + project, diagnosticIds, documentIds: [], + analyzers, + includeLocalDocumentDiagnostics: false, + includeNonLocalDocumentDiagnostics: false, + includeProjectNonLocalResult: true, + cancellationToken); + } + + private async Task> ProduceProjectDiagnosticsInProcessAsync( + Project project, ImmutableHashSet? diagnosticIds, ImmutableArray documentIds, + ImmutableArray analyzers, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, bool includeProjectNonLocalResult, @@ -28,7 +124,7 @@ public async Task> ProduceProjectDiagnosticsInPro { using var _ = ArrayBuilder.GetInstance(out var builder); - var hostAnalyzerInfo = GetOrCreateHostAnalyzerInfo(project); + var hostAnalyzerInfo = GetOrCreateHostAnalyzerInfo_OnlyCallInProcess(project); var result = await GetOrComputeDiagnosticAnalysisResultsAsync(analyzers).ConfigureAwait(false); foreach (var analyzer in analyzers) @@ -71,7 +167,7 @@ async Task> Ge ImmutableArray analyzers) { // Otherwise, just compute for the analyzers we care about. - var compilation = await GetOrCreateCompilationWithAnalyzersAsync( + var compilation = await GetOrCreateCompilationWithAnalyzers_OnlyCallInProcessAsync( project, analyzers, hostAnalyzerInfo, this.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); var result = await ComputeDiagnosticAnalysisResultsInProcessAsync( diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_RemoteOrLocalDispatcher.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_RemoteOrLocalDispatcher.cs index f3a5c56fe8d..19393e6c24c 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_RemoteOrLocalDispatcher.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_RemoteOrLocalDispatcher.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Text; @@ -19,6 +20,24 @@ namespace Microsoft.CodeAnalysis.Diagnostics; internal sealed partial class DiagnosticAnalyzerService : IDiagnosticAnalyzerService { + public async Task> ForceRunCodeAnalysisDiagnosticsAsync( + Project project, CancellationToken cancellationToken) + { + var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); + if (client is not null) + { + var descriptors = await client.TryInvokeAsync>( + project, + (service, solution, cancellationToken) => service.ForceRunCodeAnalysisDiagnosticsAsync( + solution, project.Id, cancellationToken), + cancellationToken).ConfigureAwait(false); + return descriptors.HasValue ? descriptors.Value : []; + } + + // Otherwise, fallback to computing in proc. + return await ForceRunCodeAnalysisDiagnosticsInProcessAsync(project, cancellationToken).ConfigureAwait(false); + } + public async Task> GetDiagnosticDescriptorsAsync( Solution solution, ProjectId projectId, AnalyzerReference analyzerReference, string language, CancellationToken cancellationToken) { @@ -43,34 +62,56 @@ public async Task> GetDiagnosticDescriptors .SelectManyAsArray(this._analyzerInfoCache.GetDiagnosticDescriptors); } - public async Task>> GetDiagnosticDescriptorsPerReferenceAsync(Solution solution, CancellationToken cancellationToken) + public async Task> GetCompilationEndDiagnosticDescriptorIdsAsync( + Solution solution, CancellationToken cancellationToken) { var client = await RemoteHostClient.TryGetClientAsync(solution.Services, cancellationToken).ConfigureAwait(false); if (client is not null) { - var map = await client.TryInvokeAsync>>( + var result = await client.TryInvokeAsync>( solution, - (service, solution, cancellationToken) => service.GetDiagnosticDescriptorsPerReferenceAsync(solution, cancellationToken), + (service, solution, cancellationToken) => service.GetCompilationEndDiagnosticDescriptorIdsAsync( + solution, cancellationToken), cancellationToken).ConfigureAwait(false); - if (!map.HasValue) - return ImmutableDictionary>.Empty; - return map.Value.ToImmutableDictionary( - kvp => kvp.Key, - kvp => kvp.Value.SelectAsArray(d => d.ToDiagnosticDescriptor())); + return result.HasValue ? result.Value : []; } - return solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(this._analyzerInfoCache); + using var _1 = PooledHashSet.GetInstance(out var builder); + using var _2 = PooledHashSet<(object Reference, string Language)>.GetInstance(out var seenAnalyzerReferencesByLanguage); + + foreach (var project in solution.Projects) + { + var analyzersPerReferenceMap = solution.SolutionState.Analyzers.CreateDiagnosticAnalyzersPerReference(project); + foreach (var (analyzerReference, analyzers) in analyzersPerReferenceMap) + { + if (!seenAnalyzerReferencesByLanguage.Add((analyzerReference, project.Language))) + continue; + + foreach (var analyzer in analyzers) + { + if (analyzer.IsCompilerAnalyzer()) + continue; + + foreach (var buildOnlyDescriptor in _analyzerInfoCache.GetCompilationEndDiagnosticDescriptors(analyzer)) + builder.Add(buildOnlyDescriptor.Id); + } + } + } + + return builder.ToImmutableArray(); } - public async Task>> GetDiagnosticDescriptorsPerReferenceAsync(Project project, CancellationToken cancellationToken) + public async Task>> GetDiagnosticDescriptorsPerReferenceAsync( + Solution solution, ProjectId? projectId, CancellationToken cancellationToken) { - var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); + var client = await RemoteHostClient.TryGetClientAsync(solution.Services, cancellationToken).ConfigureAwait(false); if (client is not null) { var map = await client.TryInvokeAsync>>( - project, - (service, solution, cancellationToken) => service.GetDiagnosticDescriptorsPerReferenceAsync(solution, project.Id, cancellationToken), + solution, + (service, solution, cancellationToken) => service.GetDiagnosticDescriptorsPerReferenceAsync( + solution, projectId, cancellationToken), cancellationToken).ConfigureAwait(false); if (!map.HasValue) return ImmutableDictionary>.Empty; @@ -80,101 +121,90 @@ public async Task kvp.Value.SelectAsArray(d => d.ToDiagnosticDescriptor())); } - return project.Solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(this._analyzerInfoCache, project); + return solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference( + this._analyzerInfoCache, solution.GetProject(projectId)); } - public async Task> GetDeprioritizationCandidatesAsync( - Project project, ImmutableArray analyzers, CancellationToken cancellationToken) + public async Task> GetDiagnosticsForIdsAsync( + Project project, ImmutableArray documentIds, ImmutableHashSet? diagnosticIds, AnalyzerFilter analyzerFilter, bool includeLocalDocumentDiagnostics, CancellationToken cancellationToken) { var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); if (client is not null) { - var analyzerIds = analyzers.Select(a => a.GetAnalyzerId()).ToImmutableHashSet(); - var result = await client.TryInvokeAsync>( + var result = await client.TryInvokeAsync>( project, - (service, solution, cancellationToken) => service.GetDeprioritizationCandidatesAsync( - solution, project.Id, analyzerIds, cancellationToken), + (service, solution, cancellationToken) => service.GetDiagnosticsForIdsAsync( + solution, project.Id, documentIds, diagnosticIds, analyzerFilter, includeLocalDocumentDiagnostics, cancellationToken), cancellationToken).ConfigureAwait(false); - if (!result.HasValue) - return []; - - return analyzers.FilterAnalyzers(result.Value); + return result.HasValue ? result.Value : []; } - return await GetDeprioritizationCandidatesInProcessAsync(project, analyzers, cancellationToken).ConfigureAwait(false); + return await GetDiagnosticsForIdsInProcessAsync( + project, documentIds, diagnosticIds, + analyzerFilter, + includeLocalDocumentDiagnostics, + cancellationToken).ConfigureAwait(false); } - internal async Task> ProduceProjectDiagnosticsAsync( + public async Task> GetProjectDiagnosticsForIdsAsync( Project project, - ImmutableArray analyzers, ImmutableHashSet? diagnosticIds, - ImmutableArray documentIds, - bool includeLocalDocumentDiagnostics, - bool includeNonLocalDocumentDiagnostics, - bool includeProjectNonLocalResult, + AnalyzerFilter analyzerFilter, CancellationToken cancellationToken) { var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); if (client is not null) { - var analyzerIds = analyzers.Select(a => a.GetAnalyzerId()).ToImmutableHashSet(); var result = await client.TryInvokeAsync>( project, - (service, solution, cancellationToken) => service.ProduceProjectDiagnosticsAsync( - solution, project.Id, analyzerIds, diagnosticIds, documentIds, - includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics, includeProjectNonLocalResult, - cancellationToken), + (service, solution, cancellationToken) => service.GetProjectDiagnosticsForIdsAsync( + solution, project.Id, diagnosticIds, analyzerFilter, cancellationToken), cancellationToken).ConfigureAwait(false); - if (!result.HasValue) - return []; + return result.HasValue ? result.Value : []; + } + + return await GetProjectDiagnosticsForIdsInProcessAsync( + project, diagnosticIds, analyzerFilter, cancellationToken).ConfigureAwait(false); + } - return result.Value; + public async Task IsAnyDiagnosticIdDeprioritizedAsync( + Project project, ImmutableArray diagnosticIds, CancellationToken cancellationToken) + { + var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); + if (client is not null) + { + var result = await client.TryInvokeAsync( + project, + (service, solution, cancellationToken) => service.IsAnyDiagnosticIdDeprioritizedAsync( + solution, project.Id, diagnosticIds, cancellationToken), + cancellationToken).ConfigureAwait(false); + return result.HasValue && result.Value; } - // Fallback to proccessing in proc. - return await ProduceProjectDiagnosticsInProcessAsync( - project, analyzers, diagnosticIds, documentIds, - includeLocalDocumentDiagnostics, - includeNonLocalDocumentDiagnostics, - includeProjectNonLocalResult, - cancellationToken).ConfigureAwait(false); + return await IsAnyDeprioritizedDiagnosticIdInProcessAsync( + project, diagnosticIds, cancellationToken).ConfigureAwait(false); } - public async Task> ComputeDiagnosticsAsync( + public async Task> GetDiagnosticsForSpanAsync( TextDocument document, TextSpan? range, - ImmutableArray allAnalyzers, - ImmutableArray syntaxAnalyzers, - ImmutableArray semanticSpanAnalyzers, - ImmutableArray semanticDocumentAnalyzers, - bool incrementalAnalysis, - bool logPerformanceInfo, + DiagnosticIdFilter diagnosticIdFilter, + CodeActionRequestPriority? priority, + DiagnosticKind diagnosticKind, CancellationToken cancellationToken) { - if (allAnalyzers.Length == 0) - return []; - var client = await RemoteHostClient.TryGetClientAsync(document.Project, cancellationToken).ConfigureAwait(false); if (client is not null) { - var allAnalyzerIds = allAnalyzers.Select(a => a.GetAnalyzerId()).ToImmutableHashSet(); - var syntaxAnalyzersIds = syntaxAnalyzers.Select(a => a.GetAnalyzerId()).ToImmutableHashSet(); - var semanticSpanAnalyzersIds = semanticSpanAnalyzers.Select(a => a.GetAnalyzerId()).ToImmutableHashSet(); - var semanticDocumentAnalyzersIds = semanticDocumentAnalyzers.Select(a => a.GetAnalyzerId()).ToImmutableHashSet(); - var result = await client.TryInvokeAsync>( document.Project, - (service, solution, cancellationToken) => service.ComputeDiagnosticsAsync( - solution, document.Id, range, - allAnalyzerIds, syntaxAnalyzersIds, semanticSpanAnalyzersIds, semanticDocumentAnalyzersIds, - incrementalAnalysis, logPerformanceInfo, cancellationToken), + (service, solution, cancellationToken) => service.GetDiagnosticsForSpanAsync( + solution, document.Id, range, diagnosticIdFilter, priority, diagnosticKind, cancellationToken), cancellationToken).ConfigureAwait(false); - return result.HasValue ? result.Value : []; } - return await ComputeDiagnosticsInProcessAsync( - document, range, allAnalyzers, syntaxAnalyzers, semanticSpanAnalyzers, semanticDocumentAnalyzers, - incrementalAnalysis, logPerformanceInfo, cancellationToken).ConfigureAwait(false); + return await GetDiagnosticsForSpanInProcessAsync( + document, range, diagnosticIdFilter, priority, diagnosticKind, cancellationToken).ConfigureAwait(false); } } diff --git a/src/roslyn/src/Features/Core/Portable/EditAndContinue/RudeEditDiagnosticsBuilder.cs b/src/roslyn/src/Features/Core/Portable/EditAndContinue/RudeEditDiagnosticsBuilder.cs index c0c37364579..1d0d1fb5f5d 100644 --- a/src/roslyn/src/Features/Core/Portable/EditAndContinue/RudeEditDiagnosticsBuilder.cs +++ b/src/roslyn/src/Features/Core/Portable/EditAndContinue/RudeEditDiagnosticsBuilder.cs @@ -4,8 +4,8 @@ using System; using System.Collections.Immutable; -using System.Linq; using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.EditAndContinue; @@ -44,6 +44,6 @@ public void Add(RudeEditDiagnostic diagnostic, RudeEditReportingCondition? defer public ImmutableArray GetAllDiagnostics(Func includeDeferred) => [ .. Diagnostics, - .. DeferredDiagnostics.Where(item => includeDeferred(item.diagnostic, item.condition)).Select(static item => item.diagnostic) + .. DeferredDiagnostics.SelectAsArray(item => includeDeferred(item.diagnostic, item.condition), static item => item.diagnostic) ]; } diff --git a/src/roslyn/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeLanguageDetector.cs b/src/roslyn/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeLanguageDetector.cs index ac98e96d93c..aec0a39acb1 100644 --- a/src/roslyn/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeLanguageDetector.cs +++ b/src/roslyn/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeLanguageDetector.cs @@ -28,7 +28,7 @@ internal sealed class DateAndTimeLanguageDetector( public ImmutableArray LanguageIdentifiers => ["Date", "Time", "DateTime", "DateTimeFormat"]; public DateAndTimeLanguageDetector Create(Compilation compilation, EmbeddedLanguageInfo info) - => new DateAndTimeLanguageDetector(info, compilation); + => new(info, compilation); } private const string FormatName = "format"; diff --git a/src/roslyn/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs b/src/roslyn/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs index 37359943592..514fc9a7cc6 100644 --- a/src/roslyn/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs +++ b/src/roslyn/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs @@ -421,9 +421,16 @@ private bool CheckDescendants( var syntaxFacts = Info.SyntaxFacts; var node = syntaxFacts.WalkUpParentheses(token.GetRequiredParent()); - // if we're inside some collection-like initializer, find the instance actually being created. if (syntaxFacts.IsAnyInitializerExpression(node.Parent, out var instance)) + { + // if we're inside some initializer (like `new[] { "..." }`), find the instance actually being created. node = syntaxFacts.WalkUpParentheses(instance); + } + else if (syntaxFacts.IsExpressionElement(node.Parent)) + { + // if we're inside some collection (like `["..."]`), find the instance actually being created. + node = syntaxFacts.WalkUpParentheses(node.Parent.GetRequiredParent()); + } return node; } diff --git a/src/roslyn/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncWorkItemQueue.cs b/src/roslyn/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncWorkItemQueue.cs index f83c05f0c51..d31ae5870b2 100644 --- a/src/roslyn/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncWorkItemQueue.cs +++ b/src/roslyn/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncWorkItemQueue.cs @@ -20,7 +20,7 @@ private abstract class UnitTestingAsyncWorkItemQueue(UnitTestingSolutionCr where TKey : class { private readonly object _gate = new(); - private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(initialCount: 0); + private readonly SemaphoreSlim _semaphore = new(initialCount: 0); private bool _disposed; private readonly UnitTestingSolutionCrawlerProgressReporter _progressReporter = progressReporter; diff --git a/src/roslyn/src/Features/Core/Portable/ExtractMethod/MethodExtractor.Analyzer.cs b/src/roslyn/src/Features/Core/Portable/ExtractMethod/MethodExtractor.Analyzer.cs index a61907085fa..b1ff54fcd1f 100644 --- a/src/roslyn/src/Features/Core/Portable/ExtractMethod/MethodExtractor.Analyzer.cs +++ b/src/roslyn/src/Features/Core/Portable/ExtractMethod/MethodExtractor.Analyzer.cs @@ -227,7 +227,7 @@ private OperationStatus GetOperationStatus( { var readonlyFieldStatus = CheckReadOnlyFields(symbolMap); - var namesWithAnonymousTypes = variables.Where(v => v.OriginalTypeHadAnonymousTypeOrDelegate).Select(v => v.Name ?? string.Empty); + var namesWithAnonymousTypes = variables.SelectAsArray(v => v.OriginalTypeHadAnonymousTypeOrDelegate, v => v.Name ?? string.Empty); if (returnTypeHasAnonymousType) { namesWithAnonymousTypes = namesWithAnonymousTypes.Concat("return type"); diff --git a/src/roslyn/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs b/src/roslyn/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs index 604a4f3d190..939d588a580 100644 --- a/src/roslyn/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs +++ b/src/roslyn/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs @@ -252,7 +252,7 @@ private static void AddFoldersToNamespaceContainers(List container, ILis if (folders != null && folders.Count != 0) { // Remove the empty entries and replace the spaces in the folder name to '_' - var refinedFolders = folders.Where(n => n != null && !n.IsEmpty()).Select(n => n.Replace(' ', '_')).ToArray(); + var refinedFolders = folders.SelectAsArray(n => n != null && !n.IsEmpty(), n => n.Replace(' ', '_')); container.AddRange(refinedFolders); } } diff --git a/src/roslyn/src/Features/Core/Portable/InlineHints/AbstractInlineParameterNameHintsService.cs b/src/roslyn/src/Features/Core/Portable/InlineHints/AbstractInlineParameterNameHintsService.cs index c2febd44789..f6190e6cba5 100644 --- a/src/roslyn/src/Features/Core/Portable/InlineHints/AbstractInlineParameterNameHintsService.cs +++ b/src/roslyn/src/Features/Core/Portable/InlineHints/AbstractInlineParameterNameHintsService.cs @@ -105,21 +105,18 @@ void AddHintsIfAppropriate(SyntaxNode node) if (HintMatches(kind, literalParameters, objectCreationParameters, otherParameters)) { - var inlineHintText = GetReplacementText(parameter.Name); var textSpan = new TextSpan(position, 0); - TextChange? replacementTextChange = null; - if (!parameter.IsParams) - { - replacementTextChange = new TextChange(textSpan, inlineHintText); - } + TextChange? replacementTextChange = parameter.IsParams + ? null + : new TextChange(textSpan, GetReplacementText(parameter.Name)); result.Add(new InlineHint( textSpan, [new TaggedText(TextTags.Text, parameter.Name + ": ")], replacementTextChange, ranking: InlineHintsConstants.ParameterRanking, - InlineHintHelpers.GetDescriptionFunction(position, parameter.GetSymbolKey(cancellationToken: cancellationToken), displayOptions))); + InlineHintHelpers.GetDescriptionFunction(position, parameter, displayOptions))); } } } diff --git a/src/roslyn/src/Features/Core/Portable/InlineHints/AbstractInlineTypeHintsService.cs b/src/roslyn/src/Features/Core/Portable/InlineHints/AbstractInlineTypeHintsService.cs index e7f24a25ae4..3a3744256ba 100644 --- a/src/roslyn/src/Features/Core/Portable/InlineHints/AbstractInlineTypeHintsService.cs +++ b/src/roslyn/src/Features/Core/Portable/InlineHints/AbstractInlineTypeHintsService.cs @@ -95,7 +95,7 @@ public async Task> GetInlineHintsAsync( result.Add(new InlineHint( span, taggedText, textChange, ranking: InlineHintsConstants.TypeRanking, - InlineHintHelpers.GetDescriptionFunction(spanStart, type.GetSymbolKey(cancellationToken), displayOptions))); + InlineHintHelpers.GetDescriptionFunction(spanStart, type, displayOptions))); } return result.ToImmutableAndClear(); diff --git a/src/roslyn/src/Features/Core/Portable/InlineHints/InlineHintHelpers.cs b/src/roslyn/src/Features/Core/Portable/InlineHints/InlineHintHelpers.cs index 3f7c1c32c7e..f2d0fbe097e 100644 --- a/src/roslyn/src/Features/Core/Portable/InlineHints/InlineHintHelpers.cs +++ b/src/roslyn/src/Features/Core/Portable/InlineHints/InlineHintHelpers.cs @@ -16,13 +16,16 @@ namespace Microsoft.CodeAnalysis.InlineHints; internal static class InlineHintHelpers { - public static Func>>? GetDescriptionFunction(int position, SymbolKey symbolKey, SymbolDescriptionOptions options) - => (document, cancellationToken) => GetDescriptionAsync(document, position, symbolKey, options, cancellationToken); + public static Func>>? GetDescriptionFunction(int position, ISymbol symbol, SymbolDescriptionOptions options) + { + return (document, cancellationToken) => GetDescriptionAsync(document, position, symbol, options, cancellationToken); + } - private static async Task> GetDescriptionAsync(Document document, int position, SymbolKey symbolKey, SymbolDescriptionOptions options, CancellationToken cancellationToken) + private static async Task> GetDescriptionAsync(Document document, int position, ISymbol originalSymbol, SymbolDescriptionOptions options, CancellationToken cancellationToken) { var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var symbolKey = originalSymbol.GetSymbolKey(cancellationToken); var symbol = symbolKey.Resolve(semanticModel.Compilation, cancellationToken: cancellationToken).Symbol; if (symbol != null) { diff --git a/src/roslyn/src/Features/Core/Portable/Intents/IntentDataProvider.cs b/src/roslyn/src/Features/Core/Portable/Intents/IntentDataProvider.cs index 737a21de6cc..3d7fecf6936 100644 --- a/src/roslyn/src/Features/Core/Portable/Intents/IntentDataProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/Intents/IntentDataProvider.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.Features.Intents; internal sealed class IntentDataProvider( string? serializedIntentData) { - private static readonly Lazy s_serializerOptions = new Lazy(() => + private static readonly Lazy s_serializerOptions = new(() => { var serializerOptions = new JsonSerializerOptions { diff --git a/src/roslyn/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedMethodSymbol.cs b/src/roslyn/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedMethodSymbol.cs index ee971b2a1fa..61fafff3af9 100644 --- a/src/roslyn/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedMethodSymbol.cs +++ b/src/roslyn/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedMethodSymbol.cs @@ -121,6 +121,8 @@ public IMethodSymbol ReduceExtensionMethod(ITypeSymbol receiverType) return _symbol.ReduceExtensionMethod(receiverType); } + public IMethodSymbol AssociatedExtensionImplementation => null; + public bool IsVararg => _symbol.IsVararg; public bool IsCheckedBuiltin => _symbol.IsCheckedBuiltin; diff --git a/src/roslyn/src/Features/Core/Portable/MoveStaticMembers/MoveStaticMembersWithDialogCodeAction.cs b/src/roslyn/src/Features/Core/Portable/MoveStaticMembers/MoveStaticMembersWithDialogCodeAction.cs index 11fe4ab91a4..21bc683ee77 100644 --- a/src/roslyn/src/Features/Core/Portable/MoveStaticMembers/MoveStaticMembersWithDialogCodeAction.cs +++ b/src/roslyn/src/Features/Core/Portable/MoveStaticMembers/MoveStaticMembersWithDialogCodeAction.cs @@ -119,6 +119,14 @@ protected override async Task> ComputeOperation var projectToLocations = memberReferenceLocations.ToLookup(loc => loc.location.Document.Project.Id); var solutionWithFixedReferences = await RefactorReferencesAsync(projectToLocations, newDoc.Project.Solution, newType, typeArgIndices, cancellationToken).ConfigureAwait(false); + // Qualify static member references in the moved members + solutionWithFixedReferences = await QualifyStaticMemberReferencesAsync( + solutionWithFixedReferences, + sourceDoc.Id, + memberNodes, + _selectedType, + moveOptions.SelectedMembers, + cancellationToken).ConfigureAwait(false); sourceDoc = solutionWithFixedReferences.GetRequiredDocument(sourceDoc.Id); // get back nodes from our changes @@ -194,6 +202,15 @@ private static async Task RefactorAndMoveAsync( var projectToLocations = memberReferenceLocations.ToLookup(loc => loc.location.Document.Project.Id); var solutionWithFixedReferences = await RefactorReferencesAsync(projectToLocations, oldSolution, newType, typeArgIndices, cancellationToken).ConfigureAwait(false); + // Qualify static member references in the moved members + var originalType = selectedMembers.First().ContainingType; + solutionWithFixedReferences = await QualifyStaticMemberReferencesAsync( + solutionWithFixedReferences, + sourceDocId, + oldMemberNodes, + originalType, + selectedMembers, + cancellationToken).ConfigureAwait(false); var sourceDoc = solutionWithFixedReferences.GetRequiredDocument(sourceDocId); // get back tracked nodes from our changes @@ -371,4 +388,63 @@ private static async Task FixReferencesSingleDocumentAsync( .Select(loc => (loc, refSymbol.Definition.IsExtensionMethod()))) .ToImmutableArrayOrEmpty(); } + + /// + /// Process the bodies of members being moved to qualify references to static members + /// that remain in the original class. + /// + private static async Task QualifyStaticMemberReferencesAsync( + Solution solution, + DocumentId sourceDocId, + ImmutableArray memberNodes, + INamedTypeSymbol originalType, + ImmutableArray selectedMembers, + CancellationToken cancellationToken) + { + var sourceDoc = solution.GetRequiredDocument(sourceDocId); + var root = await sourceDoc.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var semanticModel = await sourceDoc.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var syntaxFacts = sourceDoc.GetRequiredLanguageService(); + var generator = SyntaxGenerator.GetGenerator(sourceDoc); + + var members = memberNodes + .Select(node => root.GetCurrentNode(node)) + .WhereNotNull(); + var nodesToUpdate = new Dictionary(); + + // Only walking into the members being moved since those are the only places where we need to qualify + // references to static members from the original class that are not being moved. + foreach (var memberNode in members) + { + foreach (var identifierNode in memberNode.DescendantNodes().Where( + n => syntaxFacts.IsIdentifierName(n) && + !syntaxFacts.IsNameOfAnyMemberAccessExpression(n))) + { + var symbolInfo = semanticModel.GetSymbolInfo(identifierNode, cancellationToken); + var symbol = symbolInfo.Symbol; + + // Check if it's a static member from the original class that isn't being moved + if (symbol is { IsStatic: true, ContainingType: { } containingType } && + symbol.ContainingType.Name == originalType.Name && + !selectedMembers.Contains(symbol)) + { + var qualified = generator.MemberAccessExpression( + generator.TypeExpression(originalType), + identifierNode); + + nodesToUpdate.Add(identifierNode, qualified); + } + } + } + + if (nodesToUpdate.Count > 0) + { + root = root.ReplaceNodes(nodesToUpdate.Keys, + (original, _) => nodesToUpdate[original]); + + solution = solution.WithDocumentSyntaxRoot(sourceDocId, root); + } + + return solution; + } } diff --git a/src/roslyn/src/Features/Core/Portable/NavigateTo/INavigateToSearcherHost.cs b/src/roslyn/src/Features/Core/Portable/NavigateTo/INavigateToSearcherHost.cs index 5e24d607d0b..49016549712 100644 --- a/src/roslyn/src/Features/Core/Portable/NavigateTo/INavigateToSearcherHost.cs +++ b/src/roslyn/src/Features/Core/Portable/NavigateTo/INavigateToSearcherHost.cs @@ -54,7 +54,7 @@ internal sealed class DefaultNavigateToSearchHost( public async ValueTask IsFullyLoadedAsync(CancellationToken cancellationToken) { - var workspaceService = _solution.Workspace.Services.GetService(); + var workspaceService = _solution.Services.GetService(); if (workspaceService != null) return await workspaceService.IsFullyLoadedAsync(cancellationToken).ConfigureAwait(false); diff --git a/src/roslyn/src/Features/Core/Portable/OrganizeImports/OrganizeImportsCodeRefactoringProvider.cs b/src/roslyn/src/Features/Core/Portable/OrganizeImports/OrganizeImportsCodeRefactoringProvider.cs index 53c832fc623..026aafeb92c 100644 --- a/src/roslyn/src/Features/Core/Portable/OrganizeImports/OrganizeImportsCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/OrganizeImports/OrganizeImportsCodeRefactoringProvider.cs @@ -24,7 +24,7 @@ namespace Microsoft.CodeAnalysis.OrganizeImports; [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed class OrganizeImportsCodeRefactoringProvider() : SyntaxEditorBasedCodeRefactoringProvider { - protected override ImmutableArray SupportedFixAllScopes => [FixAllScope.Project, FixAllScope.Solution]; + protected override ImmutableArray SupportedRefactorAllScopes => [RefactorAllScope.Project, RefactorAllScope.Solution]; /// /// Matches 'remove unnecessary imports' code fix. This way we don't show 'sort imports' above it. @@ -51,7 +51,7 @@ protected override CodeActionRequestPriority ComputeRequestPriority() return (oldRoot, newRoot); } - protected override async Task FixAllAsync( + protected override async Task RefactorAllAsync( Document document, ImmutableArray fixAllSpans, SyntaxEditor editor, string? equivalenceKey, CancellationToken cancellationToken) { var (oldRoot, newRoot) = await RemoveImportsAsync(document, cancellationToken).ConfigureAwait(false); diff --git a/src/roslyn/src/Features/Core/Portable/PdbSourceDocument/ImplementationAssemblyLookupService.cs b/src/roslyn/src/Features/Core/Portable/PdbSourceDocument/ImplementationAssemblyLookupService.cs index 143ce4f9222..0b8e86a57ae 100644 --- a/src/roslyn/src/Features/Core/Portable/PdbSourceDocument/ImplementationAssemblyLookupService.cs +++ b/src/roslyn/src/Features/Core/Portable/PdbSourceDocument/ImplementationAssemblyLookupService.cs @@ -22,7 +22,7 @@ internal sealed class ImplementationAssemblyLookupService : IImplementationAssem { // We need to generate the namespace name in the same format that is used in metadata, which // is SymbolDisplayFormat.QualifiedNameOnlyFormat, which this is a copy of. - private static readonly SymbolDisplayFormat s_metadataSymbolDisplayFormat = new SymbolDisplayFormat( + private static readonly SymbolDisplayFormat s_metadataSymbolDisplayFormat = new( globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted, typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces); diff --git a/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQConstructedType.cs b/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQConstructedType.cs index b7c786a4671..321f15910b4 100644 --- a/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQConstructedType.cs +++ b/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQConstructedType.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.Features.RQName.Nodes; internal sealed class RQConstructedType(RQUnconstructedType definingType, IList typeArguments) : RQType { public readonly RQUnconstructedType DefiningType = definingType; - public readonly ReadOnlyCollection TypeArguments = new ReadOnlyCollection(typeArguments); + public readonly ReadOnlyCollection TypeArguments = new(typeArguments); public override SimpleTreeNode ToSimpleTree() { diff --git a/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQMethodOrProperty.cs b/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQMethodOrProperty.cs index 73a731d39ad..17d84712121 100644 --- a/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQMethodOrProperty.cs +++ b/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQMethodOrProperty.cs @@ -16,7 +16,7 @@ internal abstract class RQMethodOrProperty( IList parameters) : RQMethodPropertyOrEvent(containingType, memberName) { public readonly int TypeParameterCount = typeParameterCount; - public readonly ReadOnlyCollection Parameters = new ReadOnlyCollection(parameters); + public readonly ReadOnlyCollection Parameters = new(parameters); protected override void AppendChildren(List childList) { diff --git a/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQUnconstructedType.cs b/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQUnconstructedType.cs index e6dce7e84cf..e5f9be47133 100644 --- a/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQUnconstructedType.cs +++ b/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQUnconstructedType.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Features.RQName.Nodes; internal sealed class RQUnconstructedType(IList namespaceNames, IList typeInfos) : RQTypeOrNamespace(namespaceNames) { - public readonly ReadOnlyCollection TypeInfos = new ReadOnlyCollection(typeInfos); + public readonly ReadOnlyCollection TypeInfos = new(typeInfos); protected override string RQKeyword { diff --git a/src/roslyn/src/Features/Core/Portable/SignatureHelp/AbstractSignatureHelpProvider.cs b/src/roslyn/src/Features/Core/Portable/SignatureHelp/AbstractSignatureHelpProvider.cs index 2a6e78acfc3..21fa27cee06 100644 --- a/src/roslyn/src/Features/Core/Portable/SignatureHelp/AbstractSignatureHelpProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/SignatureHelp/AbstractSignatureHelpProvider.cs @@ -199,7 +199,7 @@ where part.Symbol.IsAnonymousType() || part.Symbol.IsTupleType() { var structuralTypeParts = new List { - new SymbolDisplayPart(SymbolDisplayPartKind.Space, null, "\r\n\r\n") + new(SymbolDisplayPartKind.Space, null, "\r\n\r\n") }; structuralTypeParts.AddRange(info.TypesParts); diff --git a/src/roslyn/src/Features/Core/Portable/Structure/BlockStructureContext.cs b/src/roslyn/src/Features/Core/Portable/Structure/BlockStructureContext.cs index 77ec758fd09..ed51879072c 100644 --- a/src/roslyn/src/Features/Core/Portable/Structure/BlockStructureContext.cs +++ b/src/roslyn/src/Features/Core/Portable/Structure/BlockStructureContext.cs @@ -13,7 +13,7 @@ internal readonly struct BlockStructureContext(SyntaxTree syntaxTree, BlockStruc { // We keep our own ObjectPool of ArrayBuilders as we want to use ArrayBuilders for their ability to efficiently create ImmutableArrays, but don't // want the maximum capacity the default pool uses for dropping items from the pool. - private static readonly ObjectPool> _blockSpanArrayBuilderPool = new ObjectPool>(() => []); + private static readonly ObjectPool> _blockSpanArrayBuilderPool = new(() => []); public readonly ArrayBuilder Spans = _blockSpanArrayBuilderPool.Allocate(); diff --git a/src/roslyn/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.Update.cs b/src/roslyn/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.Update.cs index 7d91d365b6b..3cdbe883397 100644 --- a/src/roslyn/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.Update.cs +++ b/src/roslyn/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.Update.cs @@ -98,7 +98,7 @@ private sealed class Updater(SymbolSearchUpdateEngine service, string source, st { private readonly SymbolSearchUpdateEngine _service = service; private readonly string _source = source; - private readonly DirectoryInfo _cacheDirectoryInfo = new DirectoryInfo(Path.Combine( + private readonly DirectoryInfo _cacheDirectoryInfo = new(Path.Combine( localSettingsDirectory, "PackageCache", string.Format(Invariant($"Format{AddReferenceDatabaseTextFileFormatVersion}")))); /// @@ -383,7 +383,7 @@ await RepeatIOAsync( } private static FileInfo GetBinaryFileInfo(FileInfo databaseFileInfo) - => new FileInfo(Path.ChangeExtension(databaseFileInfo.FullName, ".bin")); + => new(Path.ChangeExtension(databaseFileInfo.FullName, ".bin")); private async Task PatchLocalDatabaseAsync(FileInfo databaseFileInfo, CancellationToken cancellationToken) { diff --git a/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs b/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs index 4b638a0eacd..372a18d2605 100644 --- a/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs +++ b/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs @@ -143,7 +143,7 @@ public TestParameters WithWorkspaceKind(string workspaceKind) private const string AutoGeneratedAnalyzerConfigHeader = @"# auto-generated .editorconfig for code style options"; protected internal abstract string GetLanguage(); - protected ParenthesesOptionsProvider ParenthesesOptionsProvider => new ParenthesesOptionsProvider(this.GetLanguage()); + protected ParenthesesOptionsProvider ParenthesesOptionsProvider => new(this.GetLanguage()); protected abstract ParseOptions GetScriptOptions(); private protected virtual IDocumentServiceProvider GetDocumentServiceProvider() diff --git a/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor_OptionHelpers.cs b/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor_OptionHelpers.cs index 75890c76e73..7d730759675 100644 --- a/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor_OptionHelpers.cs +++ b/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor_OptionHelpers.cs @@ -35,20 +35,20 @@ internal static (OptionKey2, object) SingleOption(PerLanguageOption2 (new OptionKey2(option, language), codeStyle); internal OptionsCollection Option(Option2> option, T enabled, NotificationOption2 notification) - => new OptionsCollection(GetLanguage()) { { option, enabled, notification } }; + => new(GetLanguage()) { { option, enabled, notification } }; internal OptionsCollection Option(Option2> option, CodeStyleOption2 codeStyle) - => new OptionsCollection(GetLanguage()) { { option, codeStyle } }; + => new(GetLanguage()) { { option, codeStyle } }; internal OptionsCollection Option(PerLanguageOption2> option, T enabled, NotificationOption2 notification) - => new OptionsCollection(GetLanguage()) { { option, enabled, notification } }; + => new(GetLanguage()) { { option, enabled, notification } }; internal OptionsCollection Option(Option2 option, T value) - => new OptionsCollection(GetLanguage()) { { option, value } }; + => new(GetLanguage()) { { option, value } }; internal OptionsCollection Option(PerLanguageOption2 option, T value) - => new OptionsCollection(GetLanguage()) { { option, value } }; + => new(GetLanguage()) { { option, value } }; internal OptionsCollection Option(PerLanguageOption2> option, CodeStyleOption2 codeStyle) - => new OptionsCollection(GetLanguage()) { { option, codeStyle } }; + => new(GetLanguage()) { { option, codeStyle } }; } diff --git a/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionTest_NoEditor.cs b/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionTest_NoEditor.cs index c9549145cfb..82e41be0b17 100644 --- a/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionTest_NoEditor.cs +++ b/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionTest_NoEditor.cs @@ -12,6 +12,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Host.Mef; @@ -22,7 +23,6 @@ using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; using Xunit; -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; namespace Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; @@ -74,7 +74,7 @@ protected abstract CodeRefactoringProvider CreateCodeRefactoringProvider( return (actions, actionToInvoke); var fixAllCodeAction = await GetFixAllFixAsync(actionToInvoke, - refactoring.Provider, document, span, fixAllScope.Value).ConfigureAwait(false); + refactoring.Provider, document, span, fixAllScope.Value.ToRefactorAllScope()).ConfigureAwait(false); if (fixAllCodeAction == null) return ([], null); @@ -86,15 +86,15 @@ private static async Task GetFixAllFixAsync( CodeRefactoringProvider provider, Document document, TextSpan selectionSpan, - FixAllScope scope) + RefactorAllScope scope) { - var fixAllProvider = provider.GetFixAllProvider(); - if (fixAllProvider == null || !fixAllProvider.GetSupportedFixAllScopes().Contains(scope)) + var refactorAllProvider = provider.GetRefactorAllProvider(); + if (refactorAllProvider == null || !refactorAllProvider.GetSupportedRefactorAllScopes().Contains(scope)) return null; - var fixAllState = new FixAllState(fixAllProvider, document, selectionSpan, provider, scope, originalCodeAction); - var fixAllContext = new FixAllContext(fixAllState, CodeAnalysisProgress.None, CancellationToken.None); - return await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false); + var refactorAllState = new RefactorAllState(refactorAllProvider, document, selectionSpan, provider, scope, originalCodeAction); + var refactorAllContext = new RefactorAllContext(refactorAllState, CodeAnalysisProgress.None, CancellationToken.None); + return await refactorAllProvider.GetRefactoringAsync(refactorAllContext).ConfigureAwait(false); } protected override Task> GetDiagnosticsWorkerAsync(TTestWorkspace workspace, TestParameters parameters) diff --git a/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs b/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs index 0a4513fab90..e3694733a05 100644 --- a/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs +++ b/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs @@ -137,7 +137,7 @@ public int GetHashCode(Diagnostic obj) internal sealed class Analyzer : DiagnosticAnalyzer, IBuiltInAnalyzer { private readonly DiagnosticDescriptor _descriptor = - new DiagnosticDescriptor("TestId", "Test", "Test", "Test", DiagnosticSeverity.Warning, isEnabledByDefault: true); + new("TestId", "Test", "Test", "Test", DiagnosticSeverity.Warning, isEnabledByDefault: true); public bool IsHighPriority => false; diff --git a/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_NoEditor.cs b/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_NoEditor.cs index a881c715c40..5bd70ef0faa 100644 --- a/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_NoEditor.cs +++ b/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_NoEditor.cs @@ -165,7 +165,7 @@ protected static Document GetDocumentAndSelectSpan(TTestWorkspace workspace, out document, diagnostic.Location.SourceSpan, [diagnostic], - (a, d) => fixes.Add(new CodeFix(document.Project, a, d)), + (a, d) => fixes.Add(new CodeFix(a, d)), CancellationToken.None); await fixer.RegisterCodeFixesAsync(context); diff --git a/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/ParenthesesOptionsProvider.cs b/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/ParenthesesOptionsProvider.cs index 159284f1504..a120a975fdd 100644 --- a/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/ParenthesesOptionsProvider.cs +++ b/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/ParenthesesOptionsProvider.cs @@ -21,13 +21,13 @@ public ParenthesesOptionsProvider(string language) } private static readonly CodeStyleOption2 IgnorePreference = - new CodeStyleOption2(ParenthesesPreference.AlwaysForClarity, NotificationOption2.None); + new(ParenthesesPreference.AlwaysForClarity, NotificationOption2.None); private static readonly CodeStyleOption2 RequireForPrecedenceClarityPreference = - new CodeStyleOption2(ParenthesesPreference.AlwaysForClarity, NotificationOption2.Suggestion); + new(ParenthesesPreference.AlwaysForClarity, NotificationOption2.Suggestion); private static readonly CodeStyleOption2 RemoveIfUnnecessaryPreference = - new CodeStyleOption2(ParenthesesPreference.NeverIfUnnecessary, NotificationOption2.Suggestion); + new(ParenthesesPreference.NeverIfUnnecessary, NotificationOption2.Suggestion); private static IEnumerable>> GetAllExceptOtherParenthesesOptions() { diff --git a/src/roslyn/src/Features/DiagnosticsTestUtilities/NamingStyles/NamingStylesTestOptionSets.cs b/src/roslyn/src/Features/DiagnosticsTestUtilities/NamingStyles/NamingStylesTestOptionSets.cs index b6e3fafdda8..9009533e623 100644 --- a/src/roslyn/src/Features/DiagnosticsTestUtilities/NamingStyles/NamingStylesTestOptionSets.cs +++ b/src/roslyn/src/Features/DiagnosticsTestUtilities/NamingStyles/NamingStylesTestOptionSets.cs @@ -41,67 +41,67 @@ internal OptionsCollection MergeStyles(OptionsCollection first, OptionsCollectio } internal OptionsCollection ClassNamesArePascalCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, ClassNamesArePascalCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, ClassNamesArePascalCaseOption() } }; internal OptionsCollection FieldNamesAreCamelCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, FieldNamesAreCamelCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, FieldNamesAreCamelCaseOption() } }; internal OptionsCollection FieldNamesAreCamelCaseWithUnderscorePrefix - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, FieldNamesAreCamelCaseWithUnderscorePrefixOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, FieldNamesAreCamelCaseWithUnderscorePrefixOption() } }; internal OptionsCollection FieldNamesAreCamelCaseWithFieldUnderscorePrefix - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, FieldNamesAreCamelCaseWithFieldUnderscorePrefixOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, FieldNamesAreCamelCaseWithFieldUnderscorePrefixOption() } }; internal OptionsCollection FieldNamesAreCamelCaseWithFieldUnderscorePrefixAndUnderscoreEndSuffix - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, FieldNamesAreCamelCaseWithFieldUnderscorePrefixAndUnderscoreEndSuffixOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, FieldNamesAreCamelCaseWithFieldUnderscorePrefixAndUnderscoreEndSuffixOption() } }; internal OptionsCollection MethodNamesArePascalCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, MethodNamesArePascalCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, MethodNamesArePascalCaseOption() } }; internal OptionsCollection MethodNamesAreCamelCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, MethodNamesAreCamelCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, MethodNamesAreCamelCaseOption() } }; internal OptionsCollection ParameterNamesAreCamelCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, ParameterNamesAreCamelCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, ParameterNamesAreCamelCaseOption() } }; internal OptionsCollection ParameterNamesAreCamelCaseWithPUnderscorePrefix - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, ParameterNamesAreCamelCaseWithPUnderscorePrefixOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, ParameterNamesAreCamelCaseWithPUnderscorePrefixOption() } }; internal OptionsCollection ParameterNamesAreCamelCaseWithPUnderscorePrefixAndUnderscoreEndSuffix - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, ParameterNamesAreCamelCaseWithPUnderscorePrefixAndUnderscoreEndSuffixOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, ParameterNamesAreCamelCaseWithPUnderscorePrefixAndUnderscoreEndSuffixOption() } }; internal OptionsCollection LocalNamesAreCamelCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, LocalNamesAreCamelCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, LocalNamesAreCamelCaseOption() } }; internal OptionsCollection LocalFunctionNamesAreCamelCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, LocalFunctionNamesAreCamelCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, LocalFunctionNamesAreCamelCaseOption() } }; internal OptionsCollection PropertyNamesArePascalCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, PropertyNamesArePascalCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, PropertyNamesArePascalCaseOption() } }; internal OptionsCollection InterfaceNamesStartWithI - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, InterfaceNamesStartWithIOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, InterfaceNamesStartWithIOption() } }; internal OptionsCollection TypeParameterNamesStartWithT - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, TypeParameterNamesStartWithTOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, TypeParameterNamesStartWithTOption() } }; internal OptionsCollection ConstantsAreUpperCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, ConstantsAreUpperCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, ConstantsAreUpperCaseOption() } }; internal OptionsCollection LocalsAreCamelCaseConstantsAreUpperCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, LocalsAreCamelCaseConstantsAreUpperCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, LocalsAreCamelCaseConstantsAreUpperCaseOption() } }; internal OptionsCollection AsyncFunctionNamesEndWithAsync - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, AsyncFunctionNamesEndWithAsyncOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, AsyncFunctionNamesEndWithAsyncOption() } }; internal OptionsCollection MethodNamesWithAccessibilityArePascalCase(ImmutableArray accessibilities) - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, MethodNamesArePascalCaseOption(accessibilities) } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, MethodNamesArePascalCaseOption(accessibilities) } }; internal OptionsCollection SymbolKindsArePascalCase(ImmutableArray symbolKinds) - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, SymbolKindsArePascalCaseOption(symbolKinds) } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, SymbolKindsArePascalCaseOption(symbolKinds) } }; internal OptionsCollection SymbolKindsArePascalCaseEmpty() - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, SymbolKindsArePascalCaseOption([]) } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, SymbolKindsArePascalCaseOption([]) } }; internal OptionsCollection SymbolKindsArePascalCase(object symbolOrTypeKind) => SymbolKindsArePascalCase([ToSymbolKindOrTypeKind(symbolOrTypeKind)]); @@ -125,7 +125,7 @@ internal static SymbolSpecification.SymbolKindOrTypeKind ToSymbolKindOrTypeKind( } internal OptionsCollection AccessibilitiesArePascalCase(ImmutableArray accessibilities) - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, AccessibilitiesArePascalCaseOption(accessibilities) } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, AccessibilitiesArePascalCaseOption(accessibilities) } }; private static NamingStylePreferences ClassNamesArePascalCaseOption() { diff --git a/src/roslyn/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualCharSequence.cs b/src/roslyn/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualCharSequence.cs index 5e97ef64084..3749cd6fa9b 100644 --- a/src/roslyn/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualCharSequence.cs +++ b/src/roslyn/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualCharSequence.cs @@ -55,7 +55,7 @@ public bool Contains(AspNetCoreVirtualChar @char) /// public Enumerator GetEnumerator() - => new Enumerator(_virtualCharSequence.GetEnumerator()); + => new(_virtualCharSequence.GetEnumerator()); /// public struct Enumerator : IEnumerator diff --git a/src/roslyn/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs b/src/roslyn/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs index fe27c3d83ce..98e2e11de9b 100644 --- a/src/roslyn/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs +++ b/src/roslyn/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs @@ -470,7 +470,7 @@ internal static SourceText CreateTextFromFile(string path) } internal static TextSpan GetSpan(string str, string substr) - => new TextSpan(str.IndexOf(substr), substr.Length); + => new(str.IndexOf(substr), substr.Length); internal static void VerifyReadersDisposed(IEnumerable readers) { diff --git a/src/roslyn/src/Features/TestUtilities/Utils/Options.cs b/src/roslyn/src/Features/TestUtilities/Utils/Options.cs index 5fa08b4ec0c..8f2da1f4173 100644 --- a/src/roslyn/src/Features/TestUtilities/Utils/Options.cs +++ b/src/roslyn/src/Features/TestUtilities/Utils/Options.cs @@ -10,6 +10,6 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests; internal static class Options { - internal static readonly CSharpParseOptions Script = new CSharpParseOptions(kind: SourceCodeKind.Script); - internal static readonly CSharpParseOptions Regular = new CSharpParseOptions(kind: SourceCodeKind.Regular); + internal static readonly CSharpParseOptions Script = new(kind: SourceCodeKind.Script); + internal static readonly CSharpParseOptions Regular = new(kind: SourceCodeKind.Regular); } diff --git a/src/roslyn/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportFeatureService.vb b/src/roslyn/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportFeatureService.vb index e54c887c984..5daf2d1cab5 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportFeatureService.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportFeatureService.vb @@ -325,11 +325,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AddImport SyntaxFactory.QualifiedName(CreateNameSyntax(nameSpaceParts, index - 1), namePiece)) End Function - Protected Overrides Function IsViableExtensionMethod(method As IMethodSymbol, - expression As SyntaxNode, - semanticModel As SemanticModel, - syntaxFacts As ISyntaxFacts, - cancellationToken As CancellationToken) As Boolean + Protected Overrides Function IsViableExtensionMethod( + method As IMethodSymbol, + expression As SyntaxNode, + semanticModel As SemanticModel, + syntaxFacts As ISyntaxFacts, + cancellationToken As CancellationToken) As Boolean Dim leftExpressionType As ITypeSymbol If syntaxFacts.IsInvocationExpression(expression) Then leftExpressionType = semanticModel.GetEnclosingNamedType(expression.SpanStart, cancellationToken) diff --git a/src/roslyn/src/LanguageServer/ExternalAccess/VisualDiagnostics/Internal/VisualDiagnosticsServiceFactory.cs b/src/roslyn/src/LanguageServer/ExternalAccess/VisualDiagnostics/Internal/VisualDiagnosticsServiceFactory.cs index 7a1cce089f8..d2e7cc11cf3 100644 --- a/src/roslyn/src/LanguageServer/ExternalAccess/VisualDiagnostics/Internal/VisualDiagnosticsServiceFactory.cs +++ b/src/roslyn/src/LanguageServer/ExternalAccess/VisualDiagnostics/Internal/VisualDiagnosticsServiceFactory.cs @@ -29,7 +29,7 @@ internal sealed class VisualDiagnosticsServiceFactory( LspWorkspaceRegistrationService lspWorkspaceRegistrationService) : ILspServiceFactory, IOnServiceBrokerInitialized { private readonly LspWorkspaceRegistrationService _lspWorkspaceRegistrationService = lspWorkspaceRegistrationService; - private readonly Lazy _OnInitializedService = new Lazy(() => new OnInitializedService(lspWorkspaceRegistrationService)); + private readonly Lazy _OnInitializedService = new(() => new OnInitializedService(lspWorkspaceRegistrationService)); public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) { @@ -46,7 +46,7 @@ private class OnInitializedService : ILspService, IOnInitialized, IOnServiceBrok private readonly LspWorkspaceRegistrationService _lspWorkspaceRegistrationService; private IVisualDiagnosticsLanguageService? _visualDiagnosticsLanguageService; private CancellationToken _cancellationToken; - private static readonly TaskCompletionSource _taskCompletionSource = new TaskCompletionSource(); + private static readonly TaskCompletionSource _taskCompletionSource = new(); public OnInitializedService(LspWorkspaceRegistrationService lspWorkspaceRegistrationService) { diff --git a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs index 64fa762c9d5..6d0590087b5 100644 --- a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs +++ b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests; public sealed class LspFileChangeWatcherTests(ITestOutputHelper testOutputHelper) : AbstractLanguageServerHostTests(testOutputHelper) { - private readonly ClientCapabilities _clientCapabilitiesWithFileWatcherSupport = new ClientCapabilities + private readonly ClientCapabilities _clientCapabilitiesWithFileWatcherSupport = new() { Workspace = new WorkspaceClientCapabilities { diff --git a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs index 5d4dc274392..1216d9d3039 100644 --- a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs +++ b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs @@ -31,7 +31,7 @@ internal sealed class ServiceBrokerFactory private readonly ExportProvider _exportProvider; private readonly WrappedServiceBroker _wrappedServiceBroker; private Task _bridgeCompletionTask; - private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + private readonly CancellationTokenSource _cancellationTokenSource = new(); private readonly ImmutableArray _onServiceBrokerInitialized; [ImportingConstructor] diff --git a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/BrokeredServiceBridgeManifest/BrokeredServiceBridgeManifestService.cs b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/BrokeredServiceBridgeManifest/BrokeredServiceBridgeManifestService.cs index 28347ea9e1e..4c65e7361be 100644 --- a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/BrokeredServiceBridgeManifest/BrokeredServiceBridgeManifestService.cs +++ b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/BrokeredServiceBridgeManifest/BrokeredServiceBridgeManifestService.cs @@ -16,7 +16,7 @@ internal sealed class BrokeredServiceBridgeManifest : IBrokeredServiceBridgeMani { internal const string MonikerName = "Microsoft.VisualStudio.Server.IBrokeredServiceBridgeManifest"; internal const string MonikerVersion = "0.1"; - private static readonly ServiceMoniker s_serviceMoniker = new ServiceMoniker(MonikerName, new Version(MonikerVersion)); + private static readonly ServiceMoniker s_serviceMoniker = new(MonikerName, new Version(MonikerVersion)); private static readonly ServiceRpcDescriptor s_serviceDescriptor = new ServiceJsonRpcDescriptor( s_serviceMoniker, ServiceJsonRpcDescriptor.Formatters.UTF8, diff --git a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs index 251225e0572..24f2b0fbd2b 100644 --- a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs +++ b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs @@ -58,13 +58,13 @@ private sealed class FileChangeContext : IFileChangeContext /// A lock to guard updates to . Using a reader/writer lock since file change notifications can be pretty chatty /// and so we want to be able to process changes as fast as possible. /// - private readonly ReaderWriterLockSlim _watchedFilesLock = new ReaderWriterLockSlim(); + private readonly ReaderWriterLockSlim _watchedFilesLock = new(); /// /// The list of file paths we're watching manually that were outside the directories being watched. The count in this case counts /// the number of /// - private readonly Dictionary _watchedFiles = new Dictionary(s_stringComparer); + private readonly Dictionary _watchedFiles = new(s_stringComparer); private static readonly StringComparer s_stringComparer = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase; private static readonly StringComparison s_stringComparison = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; diff --git a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Services/StarredCompletions/StarredCompletionsAssemblyHelper.cs b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Services/StarredCompletions/StarredCompletionsAssemblyHelper.cs index d535fbfade1..8c65c4b7e3b 100644 --- a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Services/StarredCompletions/StarredCompletionsAssemblyHelper.cs +++ b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Services/StarredCompletions/StarredCompletionsAssemblyHelper.cs @@ -29,7 +29,7 @@ internal static class StarredCompletionAssemblyHelper /// A gate to guard the actual creation of . This just prevents us from trying to create the provider more than once; once the field is set it /// won't change again. /// - private static readonly SemaphoreSlim s_gate = new SemaphoreSlim(initialCount: 1); + private static readonly SemaphoreSlim s_gate = new(initialCount: 1); private static bool s_previousCreationFailed = false; private static CompletionProvider? s_completionProvider; diff --git a/src/roslyn/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestExampleLanguageServer.cs b/src/roslyn/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestExampleLanguageServer.cs index 864b196093d..37b16012b5a 100644 --- a/src/roslyn/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestExampleLanguageServer.cs +++ b/src/roslyn/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestExampleLanguageServer.cs @@ -73,8 +73,8 @@ public Task ShutdownAsync(string message = "Shutting down") } } - private readonly TaskCompletionSource _shuttingDown = new TaskCompletionSource(); - private readonly TaskCompletionSource _exiting = new TaskCompletionSource(); + private readonly TaskCompletionSource _shuttingDown = new(); + private readonly TaskCompletionSource _exiting = new(); protected override ILspServices ConstructLspServices() { diff --git a/src/roslyn/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs b/src/roslyn/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs index f8f0cb307fb..61b42df61ac 100644 --- a/src/roslyn/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs +++ b/src/roslyn/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs @@ -290,7 +290,7 @@ private async Task ProcessQueueAsync() var message = $"Error occurred processing queue: {ex.Message}."; if (lspServices is not null) { - await _languageServer.ShutdownAsync("Error processing queue, shutting down").ConfigureAwait(false); + await _languageServer.ShutdownAsync(message).ConfigureAwait(false); await _languageServer.ExitAsync().ConfigureAwait(false); } diff --git a/src/roslyn/src/LanguageServer/Protocol.TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs b/src/roslyn/src/LanguageServer/Protocol.TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs index 900a1dd8919..89d33544a70 100644 --- a/src/roslyn/src/LanguageServer/Protocol.TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs +++ b/src/roslyn/src/LanguageServer/Protocol.TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs @@ -42,7 +42,7 @@ private async Task> GetDiagnosticsAsync( { var text = await document.GetTextAsync().ConfigureAwait(false); var dxs = await _diagnosticAnalyzerService.GetDiagnosticsForIdsAsync( - project, [document.Id], diagnosticIds: null, shouldIncludeAnalyzer: null, + project, [document.Id], diagnosticIds: null, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); dxs = dxs.WhereAsArray(d => _includeSuppressedDiagnostics || !d.IsSuppressed); documentDiagnostics = await CodeAnalysis.Diagnostics.Extensions.ToDiagnosticsAsync( @@ -56,7 +56,7 @@ filterSpan is null if (getProjectDiagnostics) { var dxs = await _diagnosticAnalyzerService.GetDiagnosticsForIdsAsync( - project, documentIds: default, diagnosticIds: null, shouldIncludeAnalyzer: null, + project, documentIds: default, diagnosticIds: null, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); dxs = dxs.WhereAsArray(d => _includeSuppressedDiagnostics || !d.IsSuppressed); projectDiagnostics = await CodeAnalysis.Diagnostics.Extensions.ToDiagnosticsAsync(dxs.Where(d => d.DocumentId is null), project, CancellationToken.None); diff --git a/src/roslyn/src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/roslyn/src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index 9cad08424bb..ad45e29418c 100644 --- a/src/roslyn/src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/roslyn/src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -62,12 +62,12 @@ private sealed class TestSpanMapperProvider : IDocumentServiceProvider internal sealed class TestSpanMapper : ISpanMappingService { - private static readonly LinePositionSpan s_mappedLinePosition = new LinePositionSpan(new LinePosition(0, 0), new LinePosition(0, 5)); + private static readonly LinePositionSpan s_mappedLinePosition = new(new LinePosition(0, 0), new LinePosition(0, 5)); private static readonly string s_mappedFilePath = "c:\\MappedFile_\ue25b\ud86d\udeac.cs"; internal static readonly string GeneratedFileName = "GeneratedFile_\ue25b\ud86d\udeac.cs"; - internal static readonly LSP.Location MappedFileLocation = new LSP.Location + internal static readonly LSP.Location MappedFileLocation = new() { Range = ProtocolConversions.LinePositionToRange(s_mappedLinePosition), DocumentUri = ProtocolConversions.CreateAbsoluteDocumentUri(s_mappedFilePath) @@ -208,14 +208,14 @@ private protected static LSP.TextDocumentIdentifier CreateTextDocumentIdentifier } private protected static LSP.TextDocumentPositionParams CreateTextDocumentPositionParams(LSP.Location caret, ProjectId? projectContext = null) - => new LSP.TextDocumentPositionParams() + => new() { TextDocument = CreateTextDocumentIdentifier(caret.DocumentUri, projectContext), Position = caret.Range.Start }; private protected static LSP.MarkupContent CreateMarkupContent(LSP.MarkupKind kind, string value) - => new LSP.MarkupContent() + => new() { Kind = kind, Value = value @@ -226,7 +226,7 @@ private protected static LSP.CompletionParams CreateCompletionParams( LSP.VSInternalCompletionInvokeKind invokeKind, string triggerCharacter, LSP.CompletionTriggerKind triggerKind) - => new LSP.CompletionParams() + => new() { TextDocument = CreateTextDocumentIdentifier(caret.DocumentUri), Position = caret.Range.Start, @@ -284,7 +284,7 @@ private protected static LSP.CompletionParams CreateCompletionParams( } private protected static LSP.TextEdit GenerateTextEdit(string newText, int startLine, int startChar, int endLine, int endChar) - => new LSP.TextEdit + => new() { NewText = newText, Range = new LSP.Range @@ -359,9 +359,39 @@ private protected async Task CreateXmlTestLspServerAsync( return await TestLspServer.CreateAsync(workspace, lspOptions, TestOutputLspLogger); } + private void CheckForCompositionErrors(TestComposition composition) + { + // The test compositions tend to have a bunch of errors. + // We only want to fail the test if we're seeing errors in relevant parts to the language server. + // This isn't foolproof, but helps catch issues early. + + var config = composition.GetCompositionConfiguration(); + var hasLanguageServerErrors = config.CompositionErrors.Flatten().Any(error => error.Parts.Any(IsRelevantPartError)); + + if (hasLanguageServerErrors) + { + try + { + config.ThrowOnErrors(); + } + catch (CompositionFailedException ex) + { + // The ToString for the composition failed exception doesn't output a nice set of errors by default, so log it separately + this.TestOutputLspLogger.LogError($"Encountered errors in the MEF composition: {ex.Message}{Environment.NewLine}{ex.ErrorsAsString}"); + throw; + } + } + + bool IsRelevantPartError(ComposedPart part) + { + return part.Definition.Type.FullName?.Contains("Microsoft.CodeAnalysis.LanguageServer") == true; + } + } + internal async Task CreateWorkspaceAsync( InitializationOptions? options, string? workspaceKind, bool mutatingLspWorkspace, TestComposition? composition = null) { + CheckForCompositionErrors(composition ?? Composition); var workspace = new LspTestWorkspace( composition?.ExportProviderFactory.CreateExportProvider() ?? await CreateExportProviderAsync(), workspaceKind, @@ -494,7 +524,8 @@ private protected static LSP.Location GetLocationPlusOne(LSP.Location originalLo private static LSP.DidChangeTextDocumentParams CreateDidChangeTextDocumentParams( DocumentUri documentUri, - ImmutableArray<(LSP.Range Range, string Text)> changes) + ImmutableArray<(LSP.Range Range, string Text)> changes, + int version = 0) { var changeEvents = changes.Select(change => new LSP.TextDocumentContentChangeEvent { @@ -506,25 +537,27 @@ private static LSP.DidChangeTextDocumentParams CreateDidChangeTextDocumentParams { TextDocument = new LSP.VersionedTextDocumentIdentifier { - DocumentUri = documentUri + DocumentUri = documentUri, + Version = version }, ContentChanges = changeEvents }; } - private static LSP.DidOpenTextDocumentParams CreateDidOpenTextDocumentParams(DocumentUri uri, string source, string languageId = "") - => new LSP.DidOpenTextDocumentParams + private static LSP.DidOpenTextDocumentParams CreateDidOpenTextDocumentParams(DocumentUri uri, string source, string languageId = "", int version = 0) + => new() { TextDocument = new LSP.TextDocumentItem { Text = source, DocumentUri = uri, - LanguageId = languageId + LanguageId = languageId, + Version = version } }; private static LSP.DidCloseTextDocumentParams CreateDidCloseTextDocumentParams(DocumentUri uri) - => new LSP.DidCloseTextDocumentParams() + => new() { TextDocument = new LSP.TextDocumentIdentifier { @@ -704,7 +737,7 @@ public Task ExecutePreSerializedRequestAsync(string methodName, JsonDocument ser return _clientRpc.InvokeWithParameterObjectAsync(methodName, serializedRequest); } - public async Task OpenDocumentAsync(DocumentUri documentUri, string? text = null, string languageId = "") + public async Task OpenDocumentAsync(DocumentUri documentUri, string? text = null, string languageId = "", int version = 0) { if (text == null) { @@ -714,13 +747,13 @@ public async Task OpenDocumentAsync(DocumentUri documentUri, string? text = null text = sourceText.ToString(); } - var didOpenParams = CreateDidOpenTextDocumentParams(documentUri, text.ToString(), languageId); + var didOpenParams = CreateDidOpenTextDocumentParams(documentUri, text.ToString(), languageId, version); await ExecuteRequestAsync(LSP.Methods.TextDocumentDidOpenName, didOpenParams, CancellationToken.None); } /// /// Opens a document in the workspace only, and waits for workspace operations. - /// Use if the document should be opened in LSP"/> + /// Use if the document should be opened in LSP"/> /// public async Task OpenDocumentInWorkspaceAsync(DocumentId documentId, bool openAllLinkedDocuments, SourceText? text = null) { @@ -745,23 +778,34 @@ public async Task OpenDocumentInWorkspaceAsync(DocumentId documentId, bool openA await WaitForWorkspaceOperationsAsync(TestWorkspace); } - public Task ReplaceTextAsync(DocumentUri documentUri, params (LSP.Range Range, string Text)[] changes) + public Task ReplaceTextAsync(DocumentUri documentUri, int version, params (LSP.Range Range, string Text)[] changes) { var didChangeParams = CreateDidChangeTextDocumentParams( documentUri, - [.. changes]); + [.. changes], + version); return ExecuteRequestAsync(LSP.Methods.TextDocumentDidChangeName, didChangeParams, CancellationToken.None); } - public Task InsertTextAsync(DocumentUri documentUri, params (int Line, int Column, string Text)[] changes) + public Task ReplaceTextAsync(DocumentUri documentUri, params (LSP.Range Range, string Text)[] changes) { - return ReplaceTextAsync(documentUri, [.. changes.Select(change => (new LSP.Range + return ReplaceTextAsync(documentUri, version: 0, changes); + } + + public Task InsertTextAsync(DocumentUri documentUri, int version, params (int Line, int Column, string Text)[] changes) + { + return ReplaceTextAsync(documentUri, version, [.. changes.Select(change => (new LSP.Range { Start = new LSP.Position { Line = change.Line, Character = change.Column }, End = new LSP.Position { Line = change.Line, Character = change.Column } }, change.Text))]); } + public Task InsertTextAsync(DocumentUri documentUri, params (int Line, int Column, string Text)[] changes) + { + return InsertTextAsync(documentUri, version: 0, changes); + } + public Task DeleteTextAsync(DocumentUri documentUri, params (int StartLine, int StartColumn, int EndLine, int EndColumn)[] changes) { return ReplaceTextAsync(documentUri, [.. changes.Select(change => (new LSP.Range diff --git a/src/roslyn/src/LanguageServer/Protocol/Extensions/Extensions.cs b/src/roslyn/src/LanguageServer/Protocol/Extensions/Extensions.cs index b5c55b447be..f4fa2a79b2b 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Extensions/Extensions.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Extensions/Extensions.cs @@ -260,7 +260,7 @@ public static string GetMarkdownLanguageName(this Document document) } public static ClassifiedTextElement GetClassifiedText(this DefinitionItem definition) - => new ClassifiedTextElement(definition.DisplayParts.Select(part => new ClassifiedTextRun(part.Tag.ToClassificationTypeName(), part.Text))); + => new(definition.DisplayParts.Select(part => new ClassifiedTextRun(part.Tag.ToClassificationTypeName(), part.Text))); private static bool TryGetVSCompletionListSetting(ClientCapabilities clientCapabilities, [NotNullWhen(returnValue: true)] out VSInternalCompletionListSetting? completionListSetting) { diff --git a/src/roslyn/src/LanguageServer/Protocol/Extensions/ProtocolConversions.cs b/src/roslyn/src/LanguageServer/Protocol/Extensions/ProtocolConversions.cs index 2c8495ffc55..3c4c75f03dd 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Extensions/ProtocolConversions.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Extensions/ProtocolConversions.cs @@ -351,16 +351,16 @@ public static LSP.TextEdit TextChangeToTextEdit(TextChange textChange, SourceTex } public static TextChange TextEditToTextChange(LSP.TextEdit edit, SourceText oldText) - => new TextChange(RangeToTextSpan(edit.Range, oldText), edit.NewText); + => new(RangeToTextSpan(edit.Range, oldText), edit.NewText); public static TextChange ContentChangeEventToTextChange(LSP.TextDocumentContentChangeEvent changeEvent, SourceText text) - => new TextChange(RangeToTextSpan(changeEvent.Range, text), changeEvent.Text); + => new(RangeToTextSpan(changeEvent.Range, text), changeEvent.Text); public static LSP.Position LinePositionToPosition(LinePosition linePosition) - => new LSP.Position { Line = linePosition.Line, Character = linePosition.Character }; + => new() { Line = linePosition.Line, Character = linePosition.Character }; public static LSP.Range LinePositionToRange(LinePositionSpan linePositionSpan) - => new LSP.Range { Start = LinePositionToPosition(linePositionSpan.Start), End = LinePositionToPosition(linePositionSpan.End) }; + => new() { Start = LinePositionToPosition(linePositionSpan.Start), End = LinePositionToPosition(linePositionSpan.End) }; public static LSP.Range TextSpanToRange(TextSpan textSpan, SourceText text) { diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs b/src/roslyn/src/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs index 3b9d7f71048..ef52d36a238 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs @@ -209,11 +209,12 @@ private async Task ApplyCodeFixesForSpecificDiagnosticIdAsync( var tree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var range = new TextSpan(0, tree.Length); - // Compute diagnostics for everything that is not an IDE analyzer var diagnosticService = document.Project.Solution.Services.GetRequiredService(); - var diagnostics = await diagnosticService.GetDiagnosticsForSpanAsync(document, range, - shouldIncludeDiagnostic: static diagnosticId => !(IDEDiagnosticIdToOptionMappingHelper.IsKnownIDEDiagnosticId(diagnosticId)), - priorityProvider: new DefaultCodeActionRequestPriorityProvider(), + var diagnostics = await diagnosticService.GetDiagnosticsForSpanAsync( + document, range, + // Compute diagnostics for everything that is *NOT* an IDE analyzer + DiagnosticIdFilter.Exclude(IDEDiagnosticIdToOptionMappingHelper.KnownIDEDiagnosticIds), + priority: null, DiagnosticKind.All, cancellationToken).ConfigureAwait(false); diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/Options/LanguageServerProjectSystemOptionsStorage.cs b/src/roslyn/src/LanguageServer/Protocol/Features/Options/LanguageServerProjectSystemOptionsStorage.cs index 05b65aa3247..0b81fa56c4d 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Features/Options/LanguageServerProjectSystemOptionsStorage.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Features/Options/LanguageServerProjectSystemOptionsStorage.cs @@ -13,15 +13,15 @@ internal static class LanguageServerProjectSystemOptionsStorage /// /// A folder to log binlogs to when running design-time builds. /// - public static readonly Option2 BinaryLogPath = new Option2("dotnet_binary_log_path", defaultValue: null, s_optionGroup); + public static readonly Option2 BinaryLogPath = new("dotnet_binary_log_path", defaultValue: null, s_optionGroup); /// /// Whether or not automatic nuget restore is enabled. /// - public static readonly Option2 EnableAutomaticRestore = new Option2("dotnet_enable_automatic_restore", defaultValue: true, s_optionGroup); + public static readonly Option2 EnableAutomaticRestore = new("dotnet_enable_automatic_restore", defaultValue: true, s_optionGroup); /// /// Whether to use the new 'dotnet run app.cs' (file-based programs) experience. /// - public static readonly Option2 EnableFileBasedPrograms = new Option2("dotnet_enable_file_based_programs", defaultValue: true, s_optionGroup); + public static readonly Option2 EnableFileBasedPrograms = new("dotnet_enable_file_based_programs", defaultValue: true, s_optionGroup); } diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedAction.cs new file mode 100644 index 00000000000..0d82c966061 --- /dev/null +++ b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedAction.cs @@ -0,0 +1,123 @@ +// 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 Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; +using Microsoft.CodeAnalysis.CodeRefactorings; + +namespace Microsoft.CodeAnalysis.Suggestions; + +/// +/// Similar to SuggestedAction, but in a location that can be used by +/// both local Roslyn and LSP. +/// +internal sealed class SuggestedAction +{ + /// + /// Original provider that created this suggested action. This is only used for extension exception management. If + /// we encounter a (non-cancellation) exception thrown when using this provider, we wil disable it for the rest of + /// the session. + /// + public object Provider { get; } + + /// + /// Underlying responsible for making the desired change to the user's code. + /// + public CodeAction CodeAction { get; } + + /// + /// Priority that this action should be presented with. Higher priority actions should be presented more + /// prominently to the user. + /// + internal CodeActionPriority CodeActionPriority { get; } + + /// + /// If this is a code refactoring, what sort of code refactoring it is. Used to present different sorts of UI + /// affordances in certain hosts. Can be if this was not created by a code refactoring but + /// was instead created by a code fix. + /// + public CodeRefactoringKind? CodeRefactoringKind { get; } + + /// + /// If this was created to fix specific diagnostics, these are those diagnostics. This may be empty if the action + /// represents a code refactoring and not a code fix. + /// + public ImmutableArray Diagnostics { get; } + + /// + /// Optional flavors for this action. Flavors are child actions that are presented as simple links, not as + /// menu-items. For example the flavors to 'fix all in document/project/solution'. It present, will be empty and will be . + /// + public SuggestedActionFlavors? Flavors { get; } + + /// + /// Nested actions that should ideally be shown in a sub-menu under this item. This action will not itself be + /// invocable, and serves only as a named container for these sub-actions. If this is non-empty, then and will be . + /// + public ImmutableArray NestedActionSets { get; } + + /// + /// Non-null if this is a fix-all or refactor-all action. If this is non-null, then will be + /// and will be empty. + /// + public IRefactorOrFixAllState? RefactorOrFixAllState { get; } + + private SuggestedAction( + CodeAction codeAction, + CodeActionPriority codeActionPriority, + object provider, + CodeRefactoringKind? codeRefactoringKind, + ImmutableArray diagnostics, + SuggestedActionFlavors? flavors, + ImmutableArray nestedActionSets, + IRefactorOrFixAllState? refactorOrFixAllState) + { + Provider = provider; + CodeAction = codeAction; + CodeActionPriority = codeActionPriority; + CodeRefactoringKind = codeRefactoringKind; + Diagnostics = diagnostics; + Flavors = flavors; + NestedActionSets = nestedActionSets; + RefactorOrFixAllState = refactorOrFixAllState; + } + + public static SuggestedAction CreateWithFlavors( + CodeAction codeAction, + CodeActionPriority codeActionPriority, + object provider, + CodeRefactoringKind? codeRefactoringKind, + ImmutableArray diagnostics, + SuggestedActionFlavors? flavors) + { + return new(codeAction, codeActionPriority, provider, codeRefactoringKind, diagnostics, flavors, nestedActionSets: [], refactorOrFixAllState: null); + } + + public static SuggestedAction CreateWithNestedActionSets( + CodeAction codeAction, + CodeActionPriority codeActionPriority, + object provider, + CodeRefactoringKind? codeRefactoringKind, + ImmutableArray diagnostics, + ImmutableArray nestedActionSets) + { + Contract.ThrowIfTrue(nestedActionSets.IsDefaultOrEmpty); + return new(codeAction, codeActionPriority, provider, codeRefactoringKind, diagnostics, flavors: null, nestedActionSets, refactorOrFixAllState: null); + } + + public static SuggestedAction CreateRefactorOrFixAll( + CodeAction codeAction, + CodeActionPriority codeActionPriority, + CodeRefactoringKind? codeRefactoringKind, + ImmutableArray diagnostics, + IRefactorOrFixAllState refactorOrFixAllState) + { + Contract.ThrowIfNull(refactorOrFixAllState); + return new(codeAction, codeActionPriority, refactorOrFixAllState.Provider, codeRefactoringKind, diagnostics, flavors: null, nestedActionSets: [], refactorOrFixAllState); + } +} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedActionFlavors.cs b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedActionFlavors.cs new file mode 100644 index 00000000000..ad4ecc4987d --- /dev/null +++ b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedActionFlavors.cs @@ -0,0 +1,11 @@ +// 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; + +namespace Microsoft.CodeAnalysis.Suggestions; + +internal readonly record struct SuggestedActionFlavors( + string Title, + ImmutableArray Actions); diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedActionSet.cs b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedActionSet.cs new file mode 100644 index 00000000000..c0178808558 --- /dev/null +++ b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedActionSet.cs @@ -0,0 +1,27 @@ +// 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 Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Suggestions; + +/// +/// Similar to SuggestedActionSet, but in a location that can be used +/// by both local Roslyn and LSP. +/// +internal sealed class SuggestedActionSet( + string? categoryName, + ImmutableArray actions, + string? title, + CodeActionPriority priority, + TextSpan? applicableToSpan) +{ + public readonly string? CategoryName = categoryName; + public readonly ImmutableArray Actions = actions; + public readonly string? Title = title; + public readonly CodeActionPriority Priority = priority; + public readonly TextSpan? ApplicableToSpan = applicableToSpan; +} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedPredefinedSuggestedActionCategoryNames.cs b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/UnifiedPredefinedSuggestedActionCategoryNames.cs similarity index 92% rename from src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedPredefinedSuggestedActionCategoryNames.cs rename to src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/UnifiedPredefinedSuggestedActionCategoryNames.cs index d3c4b353816..8aa47eedef7 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedPredefinedSuggestedActionCategoryNames.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/UnifiedPredefinedSuggestedActionCategoryNames.cs @@ -2,7 +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. -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; +namespace Microsoft.CodeAnalysis.Suggestions; /// /// Equivalent to PredefinedSuggestedActionCategoryNames, but in a location that diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionSetComparer.cs b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/UnifiedSuggestedActionSetComparer.cs similarity index 92% rename from src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionSetComparer.cs rename to src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/UnifiedSuggestedActionSetComparer.cs index 2c0ac6a6313..b1bce2f8af0 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionSetComparer.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/UnifiedSuggestedActionSetComparer.cs @@ -8,9 +8,9 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis.Text; -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; +namespace Microsoft.CodeAnalysis.Suggestions; -internal sealed class UnifiedSuggestedActionSetComparer : IComparer +internal sealed class UnifiedSuggestedActionSetComparer : IComparer { private readonly TextSpan? _targetSpan; @@ -49,7 +49,7 @@ private static int Distance(TextSpan? maybeA, TextSpan? maybeB) return startsDistance + endsDistance; } - public int Compare(UnifiedSuggestedActionSet x, UnifiedSuggestedActionSet y) + public int Compare(SuggestedActionSet x, SuggestedActionSet y) { if (!_targetSpan.HasValue || !x.ApplicableToSpan.HasValue || !y.ApplicableToSpan.HasValue) { diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/UnifiedSuggestedActionsSource.cs similarity index 65% rename from src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs rename to src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/UnifiedSuggestedActionsSource.cs index 77219716714..8ca18bc14a8 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/UnifiedSuggestedActionsSource.cs @@ -13,47 +13,46 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; using static Microsoft.CodeAnalysis.CodeActions.CodeAction; -using CodeFixGroupKey = System.Tuple; -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; +namespace Microsoft.CodeAnalysis.Suggestions; + +using CodeFixGroupKey = (DiagnosticData diagnostic, CodeActionPriority firstPriority, CodeActionPriority? secondPriority); /// /// Provides mutual code action logic for both local and LSP scenarios -/// via intermediate interface . +/// via intermediate interface . /// internal sealed class UnifiedSuggestedActionsSource { /// /// Gets, filters, and orders code fixes. /// - public static async ValueTask> GetFilterAndOrderCodeFixesAsync( - Workspace workspace, + public static async ValueTask> GetFilterAndOrderCodeFixesAsync( ICodeFixService codeFixService, TextDocument document, TextSpan selection, - ICodeActionRequestPriorityProvider priorityProvider, + CodeActionRequestPriority? priority, CancellationToken cancellationToken) { - var originalSolution = document.Project.Solution; - // Intentionally switch to a threadpool thread to compute fixes. We do not want to accidentally run any of // this on the UI thread and potentially allow any code to take a dependency on that. await TaskScheduler.Default; var fixes = await codeFixService.GetFixesAsync( document, selection, - priorityProvider, + priority, cancellationToken).ConfigureAwait(false); var filteredFixes = fixes.WhereAsArray(c => c.Fixes.Length > 0); var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); - var organizedFixes = await OrganizeFixesAsync(workspace, originalSolution, text, filteredFixes, cancellationToken).ConfigureAwait(false); + var organizedFixes = await OrganizeFixesAsync(document.Project, text, filteredFixes, cancellationToken).ConfigureAwait(false); return organizedFixes; } @@ -61,21 +60,20 @@ public static async ValueTask> GetFilt /// /// Arrange fixes into groups based on the issue (diagnostic being fixed) and prioritize these groups. /// - private static async Task> OrganizeFixesAsync( - Workspace workspace, - Solution originalSolution, + private static async Task> OrganizeFixesAsync( + Project project, SourceText text, ImmutableArray fixCollections, CancellationToken cancellationToken) { - var map = ImmutableDictionary.CreateBuilder>(); + var map = ImmutableDictionary.CreateBuilder>(); using var _ = ArrayBuilder.GetInstance(out var order); // First group fixes by diagnostic and priority. - await GroupFixesAsync(workspace, originalSolution, fixCollections, map, order, cancellationToken).ConfigureAwait(false); + await GroupFixesAsync(project, fixCollections, map, order, cancellationToken).ConfigureAwait(false); // Then prioritize between the groups. - var prioritizedFixes = PrioritizeFixGroups(originalSolution, text, map.ToImmutable(), order.ToImmutable(), workspace); + var prioritizedFixes = PrioritizeFixGroups(text, map.ToImmutable(), order.ToImmutable()); return prioritizedFixes; } @@ -83,21 +81,19 @@ private static async Task> OrganizeFix /// Groups fixes by the diagnostic being addressed by each fix. /// private static async Task GroupFixesAsync( - Workspace workspace, - Solution originalSolution, + Project project, ImmutableArray fixCollections, - IDictionary> map, + IDictionary> map, ArrayBuilder order, CancellationToken cancellationToken) { foreach (var fixCollection in fixCollections) - await ProcessFixCollectionAsync(workspace, originalSolution, map, order, fixCollection, cancellationToken).ConfigureAwait(false); + await ProcessFixCollectionAsync(project, map, order, fixCollection, cancellationToken).ConfigureAwait(false); } private static async Task ProcessFixCollectionAsync( - Workspace workspace, - Solution originalSolution, - IDictionary> map, + Project project, + IDictionary> map, ArrayBuilder order, CodeFixCollection fixCollection, CancellationToken cancellationToken) @@ -108,125 +104,120 @@ private static async Task ProcessFixCollectionAsync( var nonSupressionCodeFixes = fixes.WhereAsArray(f => !IsTopLevelSuppressionAction(f.Action)); var supressionCodeFixes = fixes.WhereAsArray(f => IsTopLevelSuppressionAction(f.Action)); - await AddCodeActionsAsync(workspace, originalSolution, map, order, fixCollection, GetFixAllSuggestedActionSetAsync, nonSupressionCodeFixes).ConfigureAwait(false); + await AddCodeActionsAsync(project, map, order, fixCollection, GetFlavorsAsync, nonSupressionCodeFixes).ConfigureAwait(false); // Add suppression fixes to the end of a given SuggestedActionSet so that they // always show up last in a group. - await AddCodeActionsAsync(workspace, originalSolution, map, order, fixCollection, GetFixAllSuggestedActionSetAsync, supressionCodeFixes).ConfigureAwait(false); + await AddCodeActionsAsync(project, map, order, fixCollection, GetFlavorsAsync, supressionCodeFixes).ConfigureAwait(false); return; // Local functions - Task GetFixAllSuggestedActionSetAsync(CodeAction codeAction) - => GetUnifiedFixAllSuggestedActionSetAsync( - codeAction, fixCount, fixCollection.FixAllState, - fixCollection.SupportedScopes, fixCollection.FirstDiagnostic, - workspace, originalSolution, cancellationToken); + Task GetFlavorsAsync(CodeAction codeAction) + => GetUnifiedSuggestedActionFlavorsAsync( + codeAction, fixCount, fixCollection.FixAllState, fixCollection.SupportedScopes, fixCollection.Diagnostics, cancellationToken); } private static async Task AddCodeActionsAsync( - Workspace workspace, - Solution originalSolution, - IDictionary> map, + Project project, + IDictionary> map, ArrayBuilder order, CodeFixCollection fixCollection, - Func> getFixAllSuggestedActionSetAsync, + Func> getFixAllSuggestedActionSetAsync, ImmutableArray codeFixes) { foreach (var fix in codeFixes) { - var unifiedSuggestedAction = await GetUnifiedSuggestedActionAsync(originalSolution, fix.Action, fix).ConfigureAwait(false); - AddFix(fix, unifiedSuggestedAction, map, order); + var unifiedSuggestedAction = await GetUnifiedSuggestedActionAsync(project, fix.Action, fix).ConfigureAwait(false); + AddFix(project, fix, unifiedSuggestedAction, map, order); } return; // Local functions - async Task GetUnifiedSuggestedActionAsync(Solution originalSolution, CodeAction action, CodeFix fix) + async Task GetUnifiedSuggestedActionAsync(Project project, CodeAction action, CodeFix fix) { if (action.NestedActions.Length > 0) { - var unifiedNestedActions = new FixedSizeArrayBuilder(action.NestedActions.Length); + var unifiedNestedActions = new FixedSizeArrayBuilder(action.NestedActions.Length); foreach (var nestedAction in action.NestedActions) { - var unifiedNestedAction = await GetUnifiedSuggestedActionAsync(originalSolution, nestedAction, fix).ConfigureAwait(false); + var unifiedNestedAction = await GetUnifiedSuggestedActionAsync(project, nestedAction, fix).ConfigureAwait(false); unifiedNestedActions.Add(unifiedNestedAction); } - var set = new UnifiedSuggestedActionSet( - originalSolution, + var set = new SuggestedActionSet( categoryName: null, actions: unifiedNestedActions.MoveToImmutable(), title: null, priority: action.Priority, - applicableToSpan: fix.PrimaryDiagnostic.Location.SourceSpan); + applicableToSpan: fix.Diagnostics.First().Location.SourceSpan); - return new UnifiedSuggestedActionWithNestedActions( - workspace, action, action.Priority, fixCollection.Provider, [set]); + return SuggestedAction.CreateWithNestedActionSets( + action, action.Priority, fixCollection.Provider, codeRefactoringKind: null, diagnostics: [], [set]); } else { - return new UnifiedCodeFixSuggestedAction( - workspace, action, action.Priority, fix, fixCollection.Provider, + return SuggestedAction.CreateWithFlavors( + action, action.Priority, fixCollection.Provider, codeRefactoringKind: null, fix.Diagnostics, await getFixAllSuggestedActionSetAsync(action).ConfigureAwait(false)); } } } private static void AddFix( - CodeFix fix, IUnifiedSuggestedAction suggestedAction, - IDictionary> map, + Project project, + CodeFix fix, + SuggestedAction suggestedAction, + IDictionary> map, ArrayBuilder order) { - var groupKey = GetGroupKey(fix); + var groupKey = GetGroupKey(fix, project); if (!map.TryGetValue(groupKey, out var suggestedActions)) { order.Add(groupKey); - suggestedActions = ImmutableArray.CreateBuilder(); + suggestedActions = ImmutableArray.CreateBuilder(); map[groupKey] = suggestedActions; } suggestedActions.Add(suggestedAction); return; - static CodeFixGroupKey GetGroupKey(CodeFix fix) + static CodeFixGroupKey GetGroupKey(CodeFix fix, Project project) { - var diag = fix.GetPrimaryDiagnosticData(); + var diagnosticData = DiagnosticData.Create(fix.Diagnostics.First(), project); if (fix.Action is AbstractConfigurationActionWithNestedActions configurationAction) { return new CodeFixGroupKey( - diag, configurationAction.Priority, configurationAction.AdditionalPriority); + diagnosticData, configurationAction.Priority, configurationAction.AdditionalPriority); } - return new CodeFixGroupKey(diag, fix.Action.Priority, null); + return new CodeFixGroupKey(diagnosticData, fix.Action.Priority, null); } } // If the provided fix all context is non-null and the context's code action Id matches // the given code action's Id, returns the set of fix all occurrences actions associated // with the code action. - private static async Task GetUnifiedFixAllSuggestedActionSetAsync( + private static async Task GetUnifiedSuggestedActionFlavorsAsync( CodeAction action, int actionCount, - IFixAllState? fixAllState, + IRefactorOrFixAllState? fixAllState, ImmutableArray supportedScopes, - Diagnostic firstDiagnostic, - Workspace workspace, - Solution originalSolution, + ImmutableArray diagnostics, CancellationToken cancellationToken) { if (fixAllState == null) - { return null; - } if (actionCount > 1 && action.EquivalenceKey == null) - { return null; - } + + if (diagnostics is not [var firstDiagnostic, ..]) + return null; var textDocument = fixAllState.Document!; - using var fixAllSuggestedActionsDisposer = ArrayBuilder.GetInstance(out var fixAllSuggestedActions); + using var _ = ArrayBuilder.GetInstance(out var fixAllSuggestedActions); foreach (var scope in supportedScopes) { if (scope is FixAllScope.ContainingMember or FixAllScope.ContainingType) @@ -249,19 +240,13 @@ static CodeFixGroupKey GetGroupKey(CodeFix fix) } var fixAllStateForScope = fixAllState.With(scope: scope, codeActionEquivalenceKey: action.EquivalenceKey); - var fixAllSuggestedAction = new UnifiedFixAllCodeFixSuggestedAction( - workspace, action, action.Priority, fixAllStateForScope, firstDiagnostic); + var fixAllSuggestedAction = SuggestedAction.CreateRefactorOrFixAll( + action, action.Priority, codeRefactoringKind: null, diagnostics, fixAllStateForScope); fixAllSuggestedActions.Add(fixAllSuggestedAction); } - return new UnifiedSuggestedActionSet( - originalSolution, - categoryName: null, - actions: fixAllSuggestedActions.ToImmutable(), - title: CodeFixesResources.Fix_all_occurrences_in, - priority: CodeActionPriority.Lowest, - applicableToSpan: null); + return new(CodeFixesResources.Fix_all_occurrences_in, fixAllSuggestedActions.ToImmutableAndClear()); } /// @@ -269,35 +254,33 @@ static CodeFixGroupKey GetGroupKey(CodeFix fix) /// /// /// Fix groups are returned in priority order determined based on . - /// Priority for all s containing fixes is set to s containing fixes is set to by default. The only exception is the case where a only contains suppression fixes - the priority of such s is set to so that suppression + /// cref="SuggestedActionSet"/> only contains suppression fixes - the priority of such s is set to so that suppression /// fixes always show up last after all other fixes (and refactorings) for the selected line of code. /// - private static ImmutableArray PrioritizeFixGroups( - Solution originalSolution, + private static ImmutableArray PrioritizeFixGroups( SourceText text, - ImmutableDictionary> map, - ImmutableArray order, - Workspace workspace) + ImmutableDictionary> map, + ImmutableArray order) { - using var _1 = ArrayBuilder.GetInstance(out var nonSuppressionSets); - using var _2 = ArrayBuilder.GetInstance(out var suppressionSets); - using var _3 = ArrayBuilder.GetInstance(out var bulkConfigurationActions); + using var _1 = ArrayBuilder.GetInstance(out var nonSuppressionSets); + using var _2 = ArrayBuilder.GetInstance(out var suppressionSets); + using var _3 = ArrayBuilder.GetInstance(out var bulkConfigurationActions); foreach (var groupKey in order) { var actions = map[groupKey]; - var nonSuppressionActions = actions.WhereAsArray(a => !IsTopLevelSuppressionAction(a.OriginalCodeAction)); - AddUnifiedSuggestedActionsSet(originalSolution, text, nonSuppressionActions, groupKey, nonSuppressionSets); + var nonSuppressionActions = actions.WhereAsArray(a => !IsTopLevelSuppressionAction(a.CodeAction)); + AddUnifiedSuggestedActionsSet(text, nonSuppressionActions, groupKey, nonSuppressionSets); - var suppressionActions = actions.WhereAsArray(a => IsTopLevelSuppressionAction(a.OriginalCodeAction) && - !IsBulkConfigurationAction(a.OriginalCodeAction)); - AddUnifiedSuggestedActionsSet(originalSolution, text, suppressionActions, groupKey, suppressionSets); + var suppressionActions = actions.WhereAsArray(a => IsTopLevelSuppressionAction(a.CodeAction) && + !IsBulkConfigurationAction(a.CodeAction)); + AddUnifiedSuggestedActionsSet(text, suppressionActions, groupKey, suppressionSets); - bulkConfigurationActions.AddRange(actions.Where(a => IsBulkConfigurationAction(a.OriginalCodeAction))); + bulkConfigurationActions.AddRange(actions.Where(a => IsBulkConfigurationAction(a.CodeAction))); } var sets = nonSuppressionSets.ToImmutable(); @@ -305,8 +288,7 @@ private static ImmutableArray PrioritizeFixGroups( // Append bulk configuration fixes at the end of suppression/configuration fixes. if (bulkConfigurationActions.Count > 0) { - var bulkConfigurationSet = new UnifiedSuggestedActionSet( - originalSolution, + var bulkConfigurationSet = new SuggestedActionSet( UnifiedPredefinedSuggestedActionCategoryNames.CodeFix, bulkConfigurationActions.ToImmutable(), title: null, @@ -320,16 +302,23 @@ private static ImmutableArray PrioritizeFixGroups( // Wrap the suppression/configuration actions within another top level suggested action // to avoid clutter in the light bulb menu. var suppressOrConfigureCodeAction = NoChangeAction.Create(CodeFixesResources.Suppress_or_configure_issues, nameof(CodeFixesResources.Suppress_or_configure_issues)); - var wrappingSuggestedAction = new UnifiedSuggestedActionWithNestedActions( - workspace, codeAction: suppressOrConfigureCodeAction, - codeActionPriority: suppressOrConfigureCodeAction.Priority, provider: null, - nestedActionSets: suppressionSets.ToImmutable()); + + // Just pass along the provider belonging to the first action that we're offering to suppress/configure. + // This doesn't actually get used as this top level action is just a container for the nested actions + // and is never invoked itself. + var provider = suppressionSets[0].Actions[0].Provider; + var wrappingSuggestedAction = SuggestedAction.CreateWithNestedActionSets( + suppressOrConfigureCodeAction, + suppressOrConfigureCodeAction.Priority, + provider, + codeRefactoringKind: null, + diagnostics: [], + suppressionSets.ToImmutable()); // Combine the spans and the category of each of the nested suggested actions // to get the span and category for the new top level suggested action. var (span, category) = CombineSpansAndCategory(suppressionSets); - var wrappingSet = new UnifiedSuggestedActionSet( - originalSolution, + var wrappingSet = new SuggestedActionSet( category, actions: [wrappingSuggestedAction], title: CodeFixesResources.Suppress_or_configure_issues, @@ -341,7 +330,7 @@ private static ImmutableArray PrioritizeFixGroups( return sets; // Local functions - static (TextSpan? span, string category) CombineSpansAndCategory(ArrayBuilder sets) + static (TextSpan? span, string category) CombineSpansAndCategory(ArrayBuilder sets) { // We are combining the spans and categories of the given set of suggested action sets // to generate a result span containing the spans of individual suggested action sets and @@ -384,25 +373,23 @@ private static ImmutableArray PrioritizeFixGroups( } private static void AddUnifiedSuggestedActionsSet( - Solution originalSolution, SourceText text, - ImmutableArray actions, + ImmutableArray actions, CodeFixGroupKey groupKey, - ArrayBuilder sets) + ArrayBuilder sets) { foreach (var group in actions.GroupBy(a => a.CodeActionPriority)) { var priority = group.Key; // diagnostic from things like build shouldn't reach here since we don't support LB for those diagnostics - var category = GetFixCategory(groupKey.Item1.Severity); - sets.Add(new UnifiedSuggestedActionSet( - originalSolution, + var category = GetFixCategory(groupKey.diagnostic.Severity); + sets.Add(new SuggestedActionSet( category, [.. group], title: null, priority, - applicableToSpan: groupKey.Item1.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text))); + applicableToSpan: groupKey.diagnostic.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text))); } } @@ -430,8 +417,7 @@ private static bool IsBulkConfigurationAction(CodeAction action) /// /// Gets, filters, and orders code refactorings. /// - public static async Task> GetFilterAndOrderCodeRefactoringsAsync( - Workspace workspace, + public static async Task> GetFilterAndOrderCodeRefactoringsAsync( ICodeRefactoringService codeRefactoringService, TextDocument document, TextSpan selection, @@ -448,10 +434,10 @@ public static async Task> GetFilterAnd var filteredRefactorings = FilterOnAnyThread(refactorings, selection, filterOutsideSelection); - var orderedRefactorings = new FixedSizeArrayBuilder(filteredRefactorings.Length); + var orderedRefactorings = new FixedSizeArrayBuilder(filteredRefactorings.Length); foreach (var refactoring in filteredRefactorings) { - var orderedRefactoring = await OrganizeRefactoringsAsync(workspace, document, selection, refactoring, cancellationToken).ConfigureAwait(false); + var orderedRefactoring = await OrganizeRefactoringsAsync(document, selection, refactoring, cancellationToken).ConfigureAwait(false); orderedRefactorings.Add(orderedRefactoring); } @@ -497,20 +483,17 @@ bool IsActionAndSpanApplicable((CodeAction action, TextSpan? applicableSpan) act /// /// /// Refactorings are returned in priority order determined based on . - /// Priority for all s containing refactorings is set to + /// Priority for all s containing refactorings is set to /// and should show up after fixes but before /// suppression fixes in the light bulb menu. /// - private static async Task OrganizeRefactoringsAsync( - Workspace workspace, + private static async Task OrganizeRefactoringsAsync( TextDocument document, TextSpan selection, CodeRefactoring refactoring, CancellationToken cancellationToken) { - var originalSolution = document.Project.Solution; - - using var _ = ArrayBuilder.GetInstance(out var refactoringSuggestedActions); + var refactoringSuggestedActions = new FixedSizeArrayBuilder(refactoring.CodeActions.Length); foreach (var (action, applicableToSpan) in refactoring.CodeActions) { @@ -518,7 +501,7 @@ private static async Task OrganizeRefactoringsAsync( refactoringSuggestedActions.Add(unifiedActionSet); } - var actions = refactoringSuggestedActions.ToImmutable(); + var actions = refactoringSuggestedActions.MoveToImmutable(); // An action set: // - gets the the same priority as the highest priority action within in. @@ -527,46 +510,44 @@ private static async Task OrganizeRefactoringsAsync( // choice. All actions created by one Refactoring have usually the same `applicableSpan` // and therefore the complexity of determining the closest one isn't worth the benefit // of slightly more correct orderings in certain edge cases. - return new UnifiedSuggestedActionSet( - originalSolution, - UnifiedPredefinedSuggestedActionCategoryNames.Refactoring, - actions: actions, + return new SuggestedActionSet( + categoryName: UnifiedPredefinedSuggestedActionCategoryNames.Refactoring, + actions, title: null, priority: actions.Max(a => a.CodeActionPriority), - applicableToSpan: refactoring.CodeActions.FirstOrDefault().applicableToSpan); + refactoring.CodeActions.FirstOrDefault().applicableToSpan); // Local functions - async Task GetUnifiedSuggestedActionSetAsync(CodeAction codeAction, TextSpan? applicableToSpan, TextSpan selection, CancellationToken cancellationToken) + async Task GetUnifiedSuggestedActionSetAsync(CodeAction codeAction, TextSpan? applicableToSpan, TextSpan selection, CancellationToken cancellationToken) { if (codeAction.NestedActions.Length > 0) { - var nestedActions = new FixedSizeArrayBuilder(codeAction.NestedActions.Length); + var nestedActions = new FixedSizeArrayBuilder(codeAction.NestedActions.Length); foreach (var nestedAction in codeAction.NestedActions) { var unifiedAction = await GetUnifiedSuggestedActionSetAsync(nestedAction, applicableToSpan, selection, cancellationToken).ConfigureAwait(false); nestedActions.Add(unifiedAction); } - var set = new UnifiedSuggestedActionSet( - originalSolution, + var set = new SuggestedActionSet( categoryName: null, actions: nestedActions.MoveToImmutable(), title: null, priority: codeAction.Priority, applicableToSpan: applicableToSpan); - return new UnifiedSuggestedActionWithNestedActions( - workspace, codeAction, codeAction.Priority, refactoring.Provider, [set]); + return SuggestedAction.CreateWithNestedActionSets( + codeAction, codeAction.Priority, refactoring.Provider, codeRefactoringKind: null, diagnostics: [], [set]); } else { var fixAllSuggestedActionSet = await GetUnifiedFixAllSuggestedActionSetAsync(codeAction, refactoring.CodeActions.Length, document as Document, selection, refactoring.Provider, - refactoring.FixAllProviderInfo, - workspace, cancellationToken).ConfigureAwait(false); + refactoring.FixAllProviderInfo, cancellationToken).ConfigureAwait(false); - return new UnifiedCodeRefactoringSuggestedAction( - workspace, codeAction, codeAction.Priority, refactoring.Provider, fixAllSuggestedActionSet); + return SuggestedAction.CreateWithFlavors( + codeAction, codeAction.Priority, refactoring.Provider, refactoring.Provider.Kind, + diagnostics: [], fixAllSuggestedActionSet); } } } @@ -574,61 +555,48 @@ async Task GetUnifiedSuggestedActionSetAsync(CodeAction // If the provided fix all context is non-null and the context's code action Id matches // the given code action's Id, returns the set of fix all occurrences actions associated // with the code action. - private static async Task GetUnifiedFixAllSuggestedActionSetAsync( + private static async Task GetUnifiedFixAllSuggestedActionSetAsync( CodeAction action, int actionCount, Document? document, TextSpan selection, CodeRefactoringProvider provider, FixAllProviderInfo? fixAllProviderInfo, - Workspace workspace, CancellationToken cancellationToken) { if (fixAllProviderInfo == null || document == null) - { return null; - } // If the provider registered more than one code action, but provided a null equivalence key // we have no way to distinguish between which registered actions to apply or ignore for FixAll. // So, we just bail out for this case. if (actionCount > 1 && action.EquivalenceKey == null) - { return null; - } - var originalSolution = document.Project.Solution; - - using var fixAllSuggestedActionsDisposer = ArrayBuilder.GetInstance(out var fixAllSuggestedActions); + using var _ = ArrayBuilder.GetInstance(out var fixAllSuggestedActions); foreach (var scope in fixAllProviderInfo.SupportedScopes) { - var fixAllState = new CodeRefactorings.FixAllState( - (CodeRefactorings.FixAllProvider)fixAllProviderInfo.FixAllProvider, - document, selection, provider, scope, action); + var fixAllState = new RefactorAllState( + (RefactorAllProvider)fixAllProviderInfo.FixAllProvider, + document, selection, provider, scope.ToRefactorAllScope(), action); if (scope is FixAllScope.ContainingMember or FixAllScope.ContainingType) { // Skip showing ContainingMember and ContainingType FixAll scopes if the language // does not implement 'IFixAllSpanMappingService' langauge service or // we have no mapped FixAll spans to fix. - var documentsAndSpans = await fixAllState.GetFixAllSpansAsync(cancellationToken).ConfigureAwait(false); + var documentsAndSpans = await fixAllState.GetRefactorAllSpansAsync(cancellationToken).ConfigureAwait(false); if (documentsAndSpans.IsEmpty) continue; } - var fixAllSuggestedAction = new UnifiedFixAllCodeRefactoringSuggestedAction( - workspace, action, action.Priority, fixAllState); + var fixAllSuggestedAction = SuggestedAction.CreateRefactorOrFixAll( + action, action.Priority, provider.Kind, diagnostics: [], fixAllState); fixAllSuggestedActions.Add(fixAllSuggestedAction); } - return new UnifiedSuggestedActionSet( - originalSolution, - categoryName: null, - actions: fixAllSuggestedActions.ToImmutable(), - title: CodeFixesResources.Fix_all_occurrences_in, - priority: CodeActionPriority.Lowest, - applicableToSpan: null); + return new(CodeFixesResources.Fix_all_occurrences_in, fixAllSuggestedActions.ToImmutableAndClear()); } /// @@ -636,9 +604,9 @@ async Task GetUnifiedSuggestedActionSetAsync(CodeAction /// Should be called with the results from /// and . /// - public static ImmutableArray FilterAndOrderActionSets( - ImmutableArray fixes, - ImmutableArray refactorings, + public static ImmutableArray FilterAndOrderActionSets( + ImmutableArray fixes, + ImmutableArray refactorings, TextSpan? selectionOpt, int currentActionCount) { @@ -657,10 +625,10 @@ public static ImmutableArray FilterAndOrderActionSets return filteredSets; } - private static ImmutableArray GetInitiallyOrderedActionSets( + private static ImmutableArray GetInitiallyOrderedActionSets( TextSpan? selectionOpt, - ImmutableArray fixes, - ImmutableArray refactorings) + ImmutableArray fixes, + ImmutableArray refactorings) { // First, order refactorings based on the order the providers actually gave for // their actions. This way, a low pri refactoring always shows after a medium pri @@ -701,18 +669,18 @@ private static ImmutableArray GetInitiallyOrderedActi } } - private static ImmutableArray OrderActionSets( - ImmutableArray actionSets, TextSpan? selectionOpt) + private static ImmutableArray OrderActionSets( + ImmutableArray actionSets, TextSpan? selectionOpt) { return [.. actionSets.OrderByDescending(s => s.Priority).ThenBy(s => s, new UnifiedSuggestedActionSetComparer(selectionOpt))]; } - private static UnifiedSuggestedActionSet WithPriority( - UnifiedSuggestedActionSet set, CodeActionPriority priority) - => new(set.OriginalSolution, set.CategoryName, set.Actions, set.Title, priority, set.ApplicableToSpan); + private static SuggestedActionSet WithPriority( + SuggestedActionSet set, CodeActionPriority priority) + => new(set.CategoryName, set.Actions, set.Title, priority, set.ApplicableToSpan); - private static ImmutableArray InlineActionSetsIfDesirable( - ImmutableArray actionSets, + private static ImmutableArray InlineActionSetsIfDesirable( + ImmutableArray actionSets, int currentActionCount) { // If we only have a single set of items, and that set only has three max suggestion @@ -723,17 +691,15 @@ private static ImmutableArray InlineActionSetsIfDesir : actionSets.SelectAsArray(InlineActions); } - private static UnifiedSuggestedActionSet InlineActions(UnifiedSuggestedActionSet actionSet) + private static SuggestedActionSet InlineActions(SuggestedActionSet actionSet) { - using var newActionsDisposer = ArrayBuilder.GetInstance(out var newActions); + using var newActionsDisposer = ArrayBuilder.GetInstance(out var newActions); foreach (var action in actionSet.Actions) { - var actionWithNestedActions = action as UnifiedSuggestedActionWithNestedActions; - // Only inline if the underlying code action allows it. - if (actionWithNestedActions?.OriginalCodeAction.IsInlinable == true) + if (action is { CodeAction.IsInlinable: true, NestedActionSets.Length: > 0 }) { - newActions.AddRange(actionWithNestedActions.NestedActionSets.SelectMany(set => set.Actions)); + newActions.AddRange(action.NestedActionSets.SelectMany(set => set.Actions)); } else { @@ -741,8 +707,7 @@ private static UnifiedSuggestedActionSet InlineActions(UnifiedSuggestedActionSet } } - return new UnifiedSuggestedActionSet( - actionSet.OriginalSolution, + return new SuggestedActionSet( actionSet.CategoryName, newActions.ToImmutable(), actionSet.Title, @@ -750,10 +715,10 @@ private static UnifiedSuggestedActionSet InlineActions(UnifiedSuggestedActionSet actionSet.ApplicableToSpan); } - private static ImmutableArray FilterActionSetsByTitle( - ImmutableArray allActionSets) + private static ImmutableArray FilterActionSetsByTitle( + ImmutableArray allActionSets) { - using var resultDisposer = ArrayBuilder.GetInstance(out var result); + using var resultDisposer = ArrayBuilder.GetInstance(out var result); var seenTitles = new HashSet(); foreach (var set in allActionSets) @@ -768,13 +733,13 @@ private static ImmutableArray FilterActionSetsByTitle return result.ToImmutableAndClear(); } - private static UnifiedSuggestedActionSet? FilterActionSetByTitle(UnifiedSuggestedActionSet set, HashSet seenTitles) + private static SuggestedActionSet? FilterActionSetByTitle(SuggestedActionSet set, HashSet seenTitles) { - using var actionsDisposer = ArrayBuilder.GetInstance(out var actions); + using var _ = ArrayBuilder.GetInstance(out var actions); foreach (var action in set.Actions) { - if (seenTitles.Add(action.OriginalCodeAction.Title)) + if (seenTitles.Add(action.CodeAction.Title)) { actions.Add(action); } @@ -782,6 +747,6 @@ private static ImmutableArray FilterActionSetsByTitle return actions.Count == 0 ? null - : new UnifiedSuggestedActionSet(set.OriginalSolution, set.CategoryName, actions.ToImmutable(), set.Title, set.Priority, set.ApplicableToSpan); + : new SuggestedActionSet(set.CategoryName, actions.ToImmutable(), set.Title, set.Priority, set.ApplicableToSpan); } } diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionSet.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionSet.cs deleted file mode 100644 index e2ee5098bcc..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionSet.cs +++ /dev/null @@ -1,44 +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.Immutable; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; - -/// -/// Similar to SuggestedActionSet, but in a location that can be used -/// by both local Roslyn and LSP. -/// -internal sealed class UnifiedSuggestedActionSet -{ - public Solution OriginalSolution { get; } - - public string? CategoryName { get; } - - public ImmutableArray Actions { get; } - - public object? Title { get; } - - public CodeActionPriority Priority { get; } - - public TextSpan? ApplicableToSpan { get; } - - public UnifiedSuggestedActionSet( - Solution originalSolution, - string? categoryName, - ImmutableArray actions, - object? title, - CodeActionPriority priority, - TextSpan? applicableToSpan) - { - OriginalSolution = originalSolution; - CategoryName = categoryName; - Actions = actions; - Title = title; - Priority = priority; - ApplicableToSpan = applicableToSpan; - } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/ICodeFixSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/ICodeFixSuggestedAction.cs deleted file mode 100644 index 728187b5c68..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/ICodeFixSuggestedAction.cs +++ /dev/null @@ -1,16 +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 Microsoft.CodeAnalysis.CodeFixes; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; - -/// -/// Common interface used by both local Roslyn and LSP to implement -/// their specific versions of CodeFixSuggestedAction. -/// -internal interface ICodeFixSuggestedAction -{ - CodeFix CodeFix { get; } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/ICodeRefactoringSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/ICodeRefactoringSuggestedAction.cs deleted file mode 100644 index cc34589aabb..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/ICodeRefactoringSuggestedAction.cs +++ /dev/null @@ -1,16 +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 Microsoft.CodeAnalysis.CodeRefactorings; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; - -/// -/// Common interface used by both local Roslyn and LSP to implement -/// their specific versions of CodeRefactoringSuggestedAction. -/// -internal interface ICodeRefactoringSuggestedAction -{ - CodeRefactoringProvider CodeRefactoringProvider { get; } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IFixAllCodeFixSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IFixAllCodeFixSuggestedAction.cs deleted file mode 100644 index 6147f51263b..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IFixAllCodeFixSuggestedAction.cs +++ /dev/null @@ -1,21 +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 Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; - -/// -/// Common interface used by both local Roslyn and LSP to implement -/// their specific versions of FixAllCodeFixSuggestedAction. -/// -internal interface IFixAllCodeFixSuggestedAction -{ - Diagnostic Diagnostic { get; } - - CodeAction OriginalCodeAction { get; } - - IFixAllState FixAllState { get; } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IFixAllCodeRefactoringSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IFixAllCodeRefactoringSuggestedAction.cs deleted file mode 100644 index a044aa57661..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IFixAllCodeRefactoringSuggestedAction.cs +++ /dev/null @@ -1,19 +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 Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; - -/// -/// Common interface used by both local Roslyn and LSP to implement -/// their specific versions of FixAllCodeRefactoringSuggestedAction. -/// -internal interface IFixAllCodeRefactoringSuggestedAction -{ - CodeAction OriginalCodeAction { get; } - - IFixAllState FixAllState { get; } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IUnifiedSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IUnifiedSuggestedAction.cs deleted file mode 100644 index 62afaa4a830..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IUnifiedSuggestedAction.cs +++ /dev/null @@ -1,19 +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 Microsoft.CodeAnalysis.CodeActions; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; - -/// -/// Similar to ISuggestedAction, but in a location that can be used by both local Roslyn and LSP. -/// -internal interface IUnifiedSuggestedAction -{ - Workspace Workspace { get; } - - CodeAction OriginalCodeAction { get; } - - CodeActionPriority CodeActionPriority { get; } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedCodeFixSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedCodeFixSuggestedAction.cs deleted file mode 100644 index 2725cd3f17e..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedCodeFixSuggestedAction.cs +++ /dev/null @@ -1,36 +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 Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; - -/// -/// Similar to CodeFixSuggestionAction, but in a location that can be used by -/// both local Roslyn and LSP. -/// -internal sealed class UnifiedCodeFixSuggestedAction : UnifiedSuggestedAction, ICodeFixSuggestedAction -{ - public CodeFix CodeFix { get; } - - public object Provider { get; } - - public UnifiedSuggestedActionSet? FixAllFlavors { get; } - - public UnifiedCodeFixSuggestedAction( - Workspace workspace, - CodeAction codeAction, - CodeActionPriority codeActionPriority, - CodeFix codeFix, - object provider, - UnifiedSuggestedActionSet? fixAllFlavors) - : base(workspace, codeAction, codeActionPriority) - { - CodeFix = codeFix; - Provider = provider; - FixAllFlavors = fixAllFlavors; - } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedCodeRefactoringSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedCodeRefactoringSuggestedAction.cs deleted file mode 100644 index 70751f41b2b..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedCodeRefactoringSuggestedAction.cs +++ /dev/null @@ -1,32 +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 Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; - -/// -/// Similar to CodeRefactoringSuggestedAction, but in a location that can be used by -/// both local Roslyn and LSP. -/// -internal sealed class UnifiedCodeRefactoringSuggestedAction : UnifiedSuggestedAction, ICodeRefactoringSuggestedAction -{ - public CodeRefactoringProvider CodeRefactoringProvider { get; } - - public UnifiedSuggestedActionSet? FixAllFlavors { get; } - - public UnifiedCodeRefactoringSuggestedAction( - Workspace workspace, - CodeAction codeAction, - CodeActionPriority codeActionPriority, - CodeRefactoringProvider codeRefactoringProvider, - UnifiedSuggestedActionSet? fixAllFlavors) - : base(workspace, codeAction, codeActionPriority) - { - CodeRefactoringProvider = codeRefactoringProvider; - FixAllFlavors = fixAllFlavors; - } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeFixSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeFixSuggestedAction.cs deleted file mode 100644 index 652590f7872..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeFixSuggestedAction.cs +++ /dev/null @@ -1,32 +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 Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; -using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; - -/// -/// Similar to FixAllCodeFixSuggestedAction, but in a location that can be used by -/// both local Roslyn and LSP. -/// -internal sealed class UnifiedFixAllCodeFixSuggestedAction : UnifiedSuggestedAction, IFixAllCodeFixSuggestedAction -{ - public Diagnostic Diagnostic { get; } - - public IFixAllState FixAllState { get; } - - public UnifiedFixAllCodeFixSuggestedAction( - Workspace workspace, - CodeAction codeAction, - CodeActionPriority codeActionPriority, - IFixAllState fixAllState, - Diagnostic diagnostic) - : base(workspace, codeAction, codeActionPriority) - { - Diagnostic = diagnostic; - FixAllState = fixAllState; - } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs deleted file mode 100644 index ed7bb22c98b..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs +++ /dev/null @@ -1,28 +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 Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; -using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; - -/// -/// Similar to FixAllCodeRefactoringSuggestedAction, but in a location that can be used by -/// both local Roslyn and LSP. -/// -internal sealed class UnifiedFixAllCodeRefactoringSuggestedAction : UnifiedSuggestedAction, IFixAllCodeRefactoringSuggestedAction -{ - public IFixAllState FixAllState { get; } - - public UnifiedFixAllCodeRefactoringSuggestedAction( - Workspace workspace, - CodeAction codeAction, - CodeActionPriority codeActionPriority, - IFixAllState fixAllState) - : base(workspace, codeAction, codeActionPriority) - { - FixAllState = fixAllState; - } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedAction.cs deleted file mode 100644 index 17e04ceeb9b..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedAction.cs +++ /dev/null @@ -1,20 +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 Microsoft.CodeAnalysis.CodeActions; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; - -/// -/// Similar to SuggestedAction, but in a location that can be used by -/// both local Roslyn and LSP. -/// -internal class UnifiedSuggestedAction(Workspace workspace, CodeAction codeAction, CodeActionPriority codeActionPriority) : IUnifiedSuggestedAction -{ - public Workspace Workspace { get; } = workspace; - - public CodeAction OriginalCodeAction { get; } = codeAction; - - public CodeActionPriority CodeActionPriority { get; } = codeActionPriority; -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedActionWithNestedActions.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedActionWithNestedActions.cs deleted file mode 100644 index dfb653852d1..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedActionWithNestedActions.cs +++ /dev/null @@ -1,31 +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.Immutable; -using Microsoft.CodeAnalysis.CodeActions; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; - -/// -/// Similar to SuggestedActionWithNestedActions, but in a location that can be used by -/// both local Roslyn and LSP. -/// -internal sealed class UnifiedSuggestedActionWithNestedActions : UnifiedSuggestedAction -{ - public object? Provider { get; } - - public ImmutableArray NestedActionSets { get; } - - public UnifiedSuggestedActionWithNestedActions( - Workspace workspace, - CodeAction codeAction, - CodeActionPriority codeActionPriority, - object? provider, - ImmutableArray nestedActionSets) - : base(workspace, codeAction, codeActionPriority) - { - Provider = provider; - NestedActionSets = nestedActionSets; - } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/BufferedProgress.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/BufferedProgress.cs index 44c598d0290..7b7079da949 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/BufferedProgress.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/BufferedProgress.cs @@ -67,7 +67,7 @@ public void Report(T value) internal static class BufferedProgress { public static BufferedProgress Create(IProgress? progress) - => new BufferedProgress(progress); + => new(progress); public static BufferedProgress Create(IProgress? progress, Func transform) => Create(progress?.Transform(transform)); diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionFixAllResolveHandler.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionFixAllResolveHandler.cs index d6adf6f9104..d067884d0f8 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionFixAllResolveHandler.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionFixAllResolveHandler.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; @@ -53,7 +54,7 @@ public async Task HandleRequestAsync(RoslynFixAllCodeAct Contract.ThrowIfNull(data.CodeActionPath); var codeActionToResolve = CodeActionHelpers.GetCodeActionToResolve(data.CodeActionPath, codeActions, isFixAllAction: true); - var fixAllCodeAction = (FixAllCodeAction)codeActionToResolve; + var fixAllCodeAction = (RefactorOrFixAllCodeAction)codeActionToResolve; Contract.ThrowIfNull(fixAllCodeAction); var operations = await fixAllCodeAction.GetOperationsAsync(document.Project.Solution, CodeAnalysisProgress.None, cancellationToken).ConfigureAwait(false); diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs index 4e856d34cd0..c4a5481acd3 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs @@ -9,13 +9,13 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.ExtractClass; using Microsoft.CodeAnalysis.ExtractInterface; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.UnifiedSuggestions; -using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; +using Microsoft.CodeAnalysis.Suggestions; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; using CodeAction = Microsoft.CodeAnalysis.CodeActions.CodeAction; @@ -93,26 +93,26 @@ internal static class CodeActionHelpers return codeActions.ToArray(); } - private static bool IsCodeActionNotSupportedByLSP(IUnifiedSuggestedAction suggestedAction) + private static bool IsCodeActionNotSupportedByLSP(SuggestedAction suggestedAction) // Filter out code actions with options since they'll show dialogs and we can't remote the UI and the options. // Exceptions are made for ExtractClass and ExtractInterface because we have OptionsServices which // provide reasonable defaults without user interaction. - => (suggestedAction.OriginalCodeAction is CodeActionWithOptions - && suggestedAction.OriginalCodeAction is not ExtractInterfaceCodeAction - && suggestedAction.OriginalCodeAction is not ExtractClassWithDialogCodeAction) + => (suggestedAction.CodeAction is CodeActionWithOptions + && suggestedAction.CodeAction is not ExtractInterfaceCodeAction + && suggestedAction.CodeAction is not ExtractClassWithDialogCodeAction) // Skip code actions that requires non-document changes. We can't apply them in LSP currently. // https://github.com/dotnet/roslyn/issues/48698 - || suggestedAction.OriginalCodeAction.Tags.Contains(CodeAction.RequiresNonDocumentChange); + || suggestedAction.CodeAction.Tags.Contains(CodeAction.RequiresNonDocumentChange); /// /// Generate the matching code actions for . If it contains nested code actions, flatten them into an array. /// private static LSP.CodeAction[] GenerateCodeActions( CodeActionParams request, - IUnifiedSuggestedAction suggestedAction, + SuggestedAction suggestedAction, LSP.CodeActionKind codeActionKind) { - var codeAction = suggestedAction.OriginalCodeAction; + var codeAction = suggestedAction.CodeAction; var diagnosticsForFix = GetApplicableDiagnostics(request.Context, suggestedAction); using var _ = ArrayBuilder.GetInstance(out var builder); @@ -142,21 +142,21 @@ private static LSP.CodeAction[] GenerateCodeActions( CodeActionParams request, LSP.CodeActionKind codeActionKind, LSP.Diagnostic[]? diagnosticsForFix, - IUnifiedSuggestedAction suggestedAction, + SuggestedAction suggestedAction, ImmutableArray pathOfParentAction, bool isTopLevelCodeAction = false) { - var codeAction = suggestedAction.OriginalCodeAction; + var codeAction = suggestedAction.CodeAction; using var _1 = ArrayBuilder.GetInstance(out var nestedCodeActions); - if (suggestedAction is UnifiedSuggestedActionWithNestedActions unifiedSuggestedActions) + if (!suggestedAction.NestedActionSets.IsEmpty) { - foreach (var actionSet in unifiedSuggestedActions.NestedActionSets) + foreach (var actionSet in suggestedAction.NestedActionSets) { foreach (var action in actionSet.Actions) { nestedCodeActions.AddRange(CollectNestedActions(request, codeActionKind, - diagnosticsForFix, action, pathOfParentAction.Add(action.OriginalCodeAction.Title))); + diagnosticsForFix, action, pathOfParentAction.Add(action.CodeAction.Title))); } } } @@ -181,7 +181,7 @@ private static void AddLSPCodeActions( Command? nestedCodeActionCommand, ImmutableArray? nestedCodeActions, string[] codeActionPath, - IUnifiedSuggestedAction suggestedAction) + SuggestedAction suggestedAction) { var title = codeAction.Title; // We add an overarching action to the lightbulb that may contain nested actions. @@ -196,15 +196,15 @@ private static void AddLSPCodeActions( Data = new CodeActionResolveData(title, codeAction.CustomTags, request.Range, request.TextDocument, codeActionPath, fixAllFlavors: null, nestedCodeActions) }); - if (suggestedAction is UnifiedCodeFixSuggestedAction unifiedCodeFixSuggestedAction && unifiedCodeFixSuggestedAction.FixAllFlavors is not null) + if (suggestedAction is SuggestedAction { Flavors: { } fixAllFlavors }) { - var fixAllFlavors = unifiedCodeFixSuggestedAction.FixAllFlavors.Actions.OfType().Select(action => action.FixAllState.Scope.ToString()); + var flavorStrings = fixAllFlavors.Actions.Select(a => a.RefactorOrFixAllState?.Scope.ToString()).WhereNotNull(); var fixAllTitle = string.Format(FeaturesResources.Fix_All_0, title); var command = new LSP.Command { CommandIdentifier = CodeActionsHandler.RunFixAllCodeActionCommandName, Title = fixAllTitle, - Arguments = [new CodeActionResolveData(fixAllTitle, codeAction.CustomTags, request.Range, request.TextDocument, codeActionPath, [.. fixAllFlavors], nestedCodeActions: null)] + Arguments = [new CodeActionResolveData(fixAllTitle, codeAction.CustomTags, request.Range, request.TextDocument, codeActionPath, [.. flavorStrings], nestedCodeActions: null)] }; builder.Add(new LSP.CodeAction @@ -213,14 +213,14 @@ private static void AddLSPCodeActions( Command = command, Kind = codeActionKind, Diagnostics = diagnosticsForFix, - Data = new CodeActionResolveData(fixAllTitle, codeAction.CustomTags, request.Range, request.TextDocument, codeActionPath, [.. fixAllFlavors], nestedCodeActions: null) + Data = new CodeActionResolveData(fixAllTitle, codeAction.CustomTags, request.Range, request.TextDocument, codeActionPath, [.. flavorStrings], nestedCodeActions: null) }); } } private static VSInternalCodeAction GenerateVSCodeAction( CodeActionParams request, SourceText documentText, - IUnifiedSuggestedAction suggestedAction, + SuggestedAction suggestedAction, LSP.CodeActionKind codeActionKind, CodeActionPriority setPriority, LSP.Range? applicableRange, @@ -228,7 +228,7 @@ private static VSInternalCodeAction GenerateVSCodeAction( ImmutableArray codeActionPathList, ref int currentHighestSetNumber) { - var codeAction = suggestedAction.OriginalCodeAction; + var codeAction = suggestedAction.CodeAction; var diagnosticsForFix = GetApplicableDiagnostics(request.Context, suggestedAction); codeActionPathList = codeActionPathList.Add(codeAction.Title); @@ -250,18 +250,16 @@ private static VSInternalCodeAction GenerateVSCodeAction( static VSInternalCodeAction[] GenerateNestedVSCodeActions( CodeActionParams request, SourceText documentText, - IUnifiedSuggestedAction suggestedAction, + SuggestedAction suggestedAction, CodeActionKind codeActionKind, ref int currentHighestSetNumber, ImmutableArray codeActionPath) { - if (suggestedAction is not UnifiedSuggestedActionWithNestedActions suggestedActionWithNestedActions) - { + if (suggestedAction.NestedActionSets.IsEmpty) return []; - } using var _ = ArrayBuilder.GetInstance(out var nestedActions); - foreach (var nestedActionSet in suggestedActionWithNestedActions.NestedActionSets) + foreach (var nestedActionSet in suggestedAction.NestedActionSets) { // Nested code action sets should each have a unique set number that is not yet assigned to any set. var nestedSetNumber = ++currentHighestSetNumber; @@ -279,13 +277,13 @@ static VSInternalCodeAction[] GenerateNestedVSCodeActions( } } - private static LSP.Diagnostic[]? GetApplicableDiagnostics(CodeActionContext context, IUnifiedSuggestedAction action) + private static LSP.Diagnostic[]? GetApplicableDiagnostics(CodeActionContext context, SuggestedAction action) { - if (action is UnifiedCodeFixSuggestedAction codeFixAction && context.Diagnostics != null) + if (action is SuggestedAction { Diagnostics.Length: > 0 } codeFixAction && context.Diagnostics != null) { // Associate the diagnostics from the request that match the diagnostic fixed by the code action by ID. // The request diagnostics are already restricted to the code fix location by the request. - var diagnosticCodesFixedByAction = codeFixAction.CodeFix.Diagnostics.Select(d => d.Id); + var diagnosticCodesFixedByAction = codeFixAction.Diagnostics.Select(d => d.Id); using var _ = ArrayBuilder.GetInstance(out var diagnosticsBuilder); foreach (var requestDiagnostic in context.Diagnostics) { @@ -346,46 +344,41 @@ public static async Task> GetCodeActionsAsync( /// /// Generates a code action with its nested actions properly set. /// - private static CodeAction GetNestedActionsFromActionSet(IUnifiedSuggestedAction suggestedAction, string? fixAllScope) + private static CodeAction GetNestedActionsFromActionSet(SuggestedAction suggestedAction, string? fixAllScope) { - var codeAction = suggestedAction.OriginalCodeAction; - if (suggestedAction is not UnifiedSuggestedActionWithNestedActions suggestedActionWithNestedActions) - { + var codeAction = suggestedAction.CodeAction; + if (suggestedAction.NestedActionSets.IsEmpty) return codeAction; - } using var _ = ArrayBuilder.GetInstance(out var nestedActions); - foreach (var actionSet in suggestedActionWithNestedActions.NestedActionSets) + foreach (var actionSet in suggestedAction.NestedActionSets) { foreach (var action in actionSet.Actions) { nestedActions.Add(GetNestedActionsFromActionSet(action, fixAllScope)); if (fixAllScope != null) - { GetFixAllActionsFromActionSet(action, nestedActions, fixAllScope); - } } } return CodeAction.Create( - codeAction.Title, nestedActions.ToImmutable(), codeAction.IsInlinable, codeAction.Priority); + codeAction.Title, nestedActions.ToImmutableAndClear(), codeAction.IsInlinable, codeAction.Priority); } - private static void GetFixAllActionsFromActionSet(IUnifiedSuggestedAction suggestedAction, ArrayBuilder codeActions, string? fixAllScope) + private static void GetFixAllActionsFromActionSet(SuggestedAction suggestedAction, ArrayBuilder codeActions, string? fixAllScope) { - var codeAction = suggestedAction.OriginalCodeAction; - if (suggestedAction is not UnifiedCodeFixSuggestedAction { FixAllFlavors: not null } unifiedCodeFixSuggestedAction) - { + var codeAction = suggestedAction.CodeAction; + if (suggestedAction.Flavors is null) return; - } // Retrieves the fix all code action based on the scope that was selected. // Creates a FixAllCodeAction type so that we can get the correct operations for the selected scope. - var fixAllFlavor = unifiedCodeFixSuggestedAction.FixAllFlavors.Actions.OfType().Where(action => action.FixAllState.Scope.ToString() == fixAllScope).First(); - codeActions.Add(new FixAllCodeAction(codeAction.Title, fixAllFlavor.FixAllState, showPreviewChangesDialog: false)); + var fixAllFlavor = suggestedAction.Flavors.Value.Actions.Where(a => a.RefactorOrFixAllState != null && a.RefactorOrFixAllState.Scope.ToString() == fixAllScope).First(); + codeActions.Add(new RefactorOrFixAllCodeAction( + fixAllFlavor.RefactorOrFixAllState!, showPreviewChangesDialog: false, title: codeAction.Title)); } - private static async ValueTask> GetActionSetsAsync( + private static async ValueTask> GetActionSetsAsync( TextDocument document, ICodeFixService codeFixService, ICodeRefactoringService codeRefactoringService, @@ -396,12 +389,10 @@ private static async ValueTask> GetAct var textSpan = ProtocolConversions.RangeToTextSpan(selection, text); var codeFixes = await UnifiedSuggestedActionsSource.GetFilterAndOrderCodeFixesAsync( - document.Project.Solution.Workspace, codeFixService, document, textSpan, - new DefaultCodeActionRequestPriorityProvider(), - cancellationToken).ConfigureAwait(false); + codeFixService, document, textSpan, priority: null, cancellationToken).ConfigureAwait(false); var codeRefactorings = await UnifiedSuggestedActionsSource.GetFilterAndOrderCodeRefactoringsAsync( - document.Project.Solution.Workspace, codeRefactoringService, document, textSpan, priority: null, + codeRefactoringService, document, textSpan, priority: null, filterOutsideSelection: false, cancellationToken).ConfigureAwait(false); var actionSets = UnifiedSuggestedActionsSource.FilterAndOrderActionSets( @@ -409,7 +400,7 @@ private static async ValueTask> GetAct return actionSets; } - private static CodeActionKind GetCodeActionKindFromSuggestedActionCategoryName(string categoryName, IUnifiedSuggestedAction suggestedAction) + private static CodeActionKind GetCodeActionKindFromSuggestedActionCategoryName(string categoryName, SuggestedAction suggestedAction) => categoryName switch { UnifiedPredefinedSuggestedActionCategoryNames.CodeFix => CodeActionKind.QuickFix, @@ -419,18 +410,13 @@ private static CodeActionKind GetCodeActionKindFromSuggestedActionCategoryName(s _ => throw ExceptionUtilities.UnexpectedValue(categoryName) }; - private static CodeActionKind GetRefactoringKind(IUnifiedSuggestedAction suggestedAction) - { - if (suggestedAction is not ICodeRefactoringSuggestedAction refactoringAction) - return CodeActionKind.Refactor; - - return refactoringAction.CodeRefactoringProvider.Kind switch + private static CodeActionKind GetRefactoringKind(SuggestedAction suggestedAction) + => suggestedAction.CodeRefactoringKind switch { CodeRefactoringKind.Extract => CodeActionKind.RefactorExtract, CodeRefactoringKind.Inline => CodeActionKind.RefactorInline, _ => CodeActionKind.Refactor, }; - } private static LSP.VSInternalPriorityLevel? UnifiedSuggestedActionSetPriorityToPriorityLevel(CodeActionPriority priority) => priority switch @@ -461,7 +447,7 @@ public static CodeAction GetCodeActionToResolve(string[] codeActionPath, Immutab // Otherwise, we are likely at the end of the path and need to retrieve // the FixAllCodeAction if we are in that state or just the regular CodeAction // since they have the same title path. - matchingAction = matchingActions.Single(action => isFixAllAction ? action is FixAllCodeAction : action is CodeAction); + matchingAction = matchingActions.Single(action => isFixAllAction ? action is RefactorOrFixAllCodeAction : action is CodeAction); } Contract.ThrowIfNull(matchingAction); diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/FixAllCodeAction.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/FixAllCodeAction.cs deleted file mode 100644 index 7b2ab074dca..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/FixAllCodeAction.cs +++ /dev/null @@ -1,29 +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.Threading; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; - -namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; - -internal sealed class FixAllCodeAction : AbstractFixAllCodeAction -{ - private readonly string _title; - - public FixAllCodeAction(string title, IFixAllState fixAllState, bool showPreviewChangesDialog) : base(fixAllState, showPreviewChangesDialog) - { - _title = title; - } - - public override string Title - => _title; - - protected override IFixAllContext CreateFixAllContext(IFixAllState fixAllState, IProgress progressTracker, CancellationToken cancellationToken) - => new FixAllContext((FixAllState)fixAllState, progressTracker, cancellationToken); - - protected override bool IsInternalProvider(IFixAllState fixAllState) - => true; // FixAll support is internal for the language server. -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/BuildOnlyDiagnosticIdsHandler.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/BuildOnlyDiagnosticIdsHandler.cs index e67b79b27d7..0ed0cc6349c 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/BuildOnlyDiagnosticIdsHandler.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/BuildOnlyDiagnosticIdsHandler.cs @@ -13,7 +13,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.Handler; @@ -24,56 +23,33 @@ internal sealed record class BuildOnlyDiagnosticIdsResult([property: JsonPropert [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed class BuildOnlyDiagnosticIdsHandler( - DiagnosticAnalyzerInfoCache.SharedGlobalCache globalCache, [ImportMany] IEnumerable> compilerBuildOnlyDiagnosticsProviders) - : ILspServiceRequestHandler + : ILspServiceRequestHandler { public const string BuildOnlyDiagnosticIdsMethodName = "workspace/buildOnlyDiagnosticIds"; - private readonly DiagnosticAnalyzerInfoCache.SharedGlobalCache _globalCache = globalCache; private readonly ImmutableDictionary _compilerBuildOnlyDiagnosticIds = compilerBuildOnlyDiagnosticsProviders .ToImmutableDictionary(lazy => lazy.Metadata.LanguageName, lazy => lazy.Metadata.BuildOnlyDiagnostics); public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public Task HandleRequestAsync(RequestContext context, CancellationToken cancellationToken) + public async Task HandleRequestAsync(RequestContext context, CancellationToken cancellationToken) { - Contract.ThrowIfNull(context.Solution); + var solution = context.Solution; + Contract.ThrowIfNull(solution); - using var _1 = ArrayBuilder.GetInstance(out var builder); - foreach (var languageName in context.Solution.Projects.Select(p => p.Language).Distinct()) + using var _ = ArrayBuilder.GetInstance(out var builder); + foreach (var languageName in solution.Projects.Select(p => p.Language).Distinct()) { if (_compilerBuildOnlyDiagnosticIds.TryGetValue(languageName, out var compilerBuildOnlyDiagnosticIds)) - { builder.AddRange(compilerBuildOnlyDiagnosticIds); - } } - using var _2 = PooledHashSet<(object Reference, string Language)>.GetInstance(out var seenAnalyzerReferencesByLanguage); + var diagnosticService = solution.Services.GetRequiredService(); + builder.AddRange(await diagnosticService.GetCompilationEndDiagnosticDescriptorIdsAsync( + solution, cancellationToken).ConfigureAwait(false)); - foreach (var project in context.Solution.Projects) - { - var analyzersPerReferenceMap = context.Solution.SolutionState.Analyzers.CreateDiagnosticAnalyzersPerReference(project); - foreach (var (analyzerReference, analyzers) in analyzersPerReferenceMap) - { - if (!seenAnalyzerReferencesByLanguage.Add((analyzerReference, project.Language))) - continue; - - foreach (var analyzer in analyzers) - { - // We have already added the compiler build-only diagnostics upfront. - if (analyzer.IsCompilerAnalyzer()) - continue; - - foreach (var buildOnlyDescriptor in _globalCache.AnalyzerInfoCache.GetCompilationEndDiagnosticDescriptors(analyzer)) - { - builder.Add(buildOnlyDescriptor.Id); - } - } - } - } - - return Task.FromResult(new BuildOnlyDiagnosticIdsResult(builder.ToArray())); + return new BuildOnlyDiagnosticIdsResult(builder.ToArray()); } } diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSourceProviders/WorkspaceDocumentsAndProjectDiagnosticSourceProvider.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSourceProviders/WorkspaceDocumentsAndProjectDiagnosticSourceProvider.cs index d6f95565292..c593cab2cdc 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSourceProviders/WorkspaceDocumentsAndProjectDiagnosticSourceProvider.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSourceProviders/WorkspaceDocumentsAndProjectDiagnosticSourceProvider.cs @@ -67,8 +67,9 @@ async Task AddDocumentsAndProjectAsync(Project project, CancellationToken cancel if (!fullSolutionAnalysisEnabled && !codeAnalysisService.HasProjectBeenAnalyzed(project.Id)) return; - Func? shouldIncludeAnalyzer = !compilerFullSolutionAnalysisEnabled || !analyzersFullSolutionAnalysisEnabled - ? ShouldIncludeAnalyzer : null; + var filter = + (compilerFullSolutionAnalysisEnabled ? AnalyzerFilter.CompilerAnalyzer : 0) | + (analyzersFullSolutionAnalysisEnabled ? AnalyzerFilter.NonCompilerAnalyzer : 0); AddDocumentSources(project.Documents); AddDocumentSources(project.AdditionalDocuments); @@ -89,7 +90,7 @@ void AddDocumentSources(IEnumerable documents) { // Add the appropriate FSA or CodeAnalysis document source to get document diagnostics. var documentDiagnosticSource = fullSolutionAnalysisEnabled - ? AbstractWorkspaceDocumentDiagnosticSource.CreateForFullSolutionAnalysisDiagnostics(document, shouldIncludeAnalyzer) + ? AbstractWorkspaceDocumentDiagnosticSource.CreateForFullSolutionAnalysisDiagnostics(document, filter) : AbstractWorkspaceDocumentDiagnosticSource.CreateForCodeAnalysisDiagnostics(document, codeAnalysisService); result.Add(documentDiagnosticSource); } @@ -99,13 +100,10 @@ void AddDocumentSources(IEnumerable documents) void AddProjectSource() { var projectDiagnosticSource = fullSolutionAnalysisEnabled - ? AbstractProjectDiagnosticSource.CreateForFullSolutionAnalysisDiagnostics(project, shouldIncludeAnalyzer) + ? AbstractProjectDiagnosticSource.CreateForFullSolutionAnalysisDiagnostics(project, filter) : AbstractProjectDiagnosticSource.CreateForCodeAnalysisDiagnostics(project, codeAnalysisService); result.Add(projectDiagnosticSource); } - - bool ShouldIncludeAnalyzer(DiagnosticAnalyzer analyzer) - => analyzer.IsCompilerAnalyzer() ? compilerFullSolutionAnalysisEnabled : analyzersFullSolutionAnalysisEnabled; } } } diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractProjectDiagnosticSource.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractProjectDiagnosticSource.cs index 30df0450623..fd3cd71ac1f 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractProjectDiagnosticSource.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractProjectDiagnosticSource.cs @@ -18,8 +18,8 @@ internal abstract class AbstractProjectDiagnosticSource(Project project) protected Project Project => project; protected Solution Solution => this.Project.Solution; - public static AbstractProjectDiagnosticSource CreateForFullSolutionAnalysisDiagnostics(Project project, Func? shouldIncludeAnalyzer) - => new FullSolutionAnalysisDiagnosticSource(project, shouldIncludeAnalyzer); + public static AbstractProjectDiagnosticSource CreateForFullSolutionAnalysisDiagnostics(Project project, AnalyzerFilter analyzerFilter) + => new FullSolutionAnalysisDiagnosticSource(project, analyzerFilter); public static AbstractProjectDiagnosticSource CreateForCodeAnalysisDiagnostics(Project project, ICodeAnalysisDiagnosticAnalyzerService codeAnalysisService) => new CodeAnalysisDiagnosticSource(project, codeAnalysisService); @@ -35,7 +35,7 @@ public static AbstractProjectDiagnosticSource CreateForCodeAnalysisDiagnostics(P public string ToDisplayString() => Project.Name; private sealed class FullSolutionAnalysisDiagnosticSource( - Project project, Func? shouldIncludeAnalyzer) + Project project, AnalyzerFilter analyzerFilter) : AbstractProjectDiagnosticSource(project) { public override async Task> GetDiagnosticsAsync( @@ -47,9 +47,8 @@ public override async Task> GetDiagnosticsAsync( // it will be computed on demand. Because it is always accurate as per this snapshot, all spans are correct // and do not need to be adjusted. var service = this.Solution.Services.GetRequiredService(); - var filter = service.GetDefaultAnalyzerFilter(Project, diagnosticIds: null, shouldIncludeAnalyzer); var diagnostics = await service.GetProjectDiagnosticsForIdsAsync( - Project, diagnosticIds: null, filter, cancellationToken).ConfigureAwait(false); + Project, diagnosticIds: null, analyzerFilter, cancellationToken).ConfigureAwait(false); // TODO(cyrusn): In the future we could consider reporting these, but with a flag on the diagnostic mentioning // that it is suppressed and should be hidden from the task list by default. diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs index a75c275ce26..73dca2b9ee8 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs @@ -15,14 +15,14 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; internal abstract class AbstractWorkspaceDocumentDiagnosticSource(TextDocument document) : AbstractDocumentDiagnosticSource(document) { - public static AbstractWorkspaceDocumentDiagnosticSource CreateForFullSolutionAnalysisDiagnostics(TextDocument document, Func? shouldIncludeAnalyzer) - => new FullSolutionAnalysisDiagnosticSource(document, shouldIncludeAnalyzer); + public static AbstractWorkspaceDocumentDiagnosticSource CreateForFullSolutionAnalysisDiagnostics(TextDocument document, AnalyzerFilter analyzerFilter) + => new FullSolutionAnalysisDiagnosticSource(document, analyzerFilter); public static AbstractWorkspaceDocumentDiagnosticSource CreateForCodeAnalysisDiagnostics(TextDocument document, ICodeAnalysisDiagnosticAnalyzerService codeAnalysisService) => new CodeAnalysisDiagnosticSource(document, codeAnalysisService); private sealed class FullSolutionAnalysisDiagnosticSource( - TextDocument document, Func? shouldIncludeAnalyzer) + TextDocument document, AnalyzerFilter analyzerFilter) : AbstractWorkspaceDocumentDiagnosticSource(document) { /// @@ -71,10 +71,8 @@ AsyncLazy> GetLazyDiagnostics() async cancellationToken => { var service = this.Solution.Services.GetRequiredService(); - var filter = service.GetDefaultAnalyzerFilter( - project, diagnosticIds: null, shouldIncludeAnalyzer); var allDiagnostics = await service.GetDiagnosticsForIdsAsync( - project, documentIds: default, diagnosticIds: null, filter, includeLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false); + project, documentIds: default, diagnosticIds: null, analyzerFilter, includeLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false); // TODO(cyrusn): Should we be filtering out suppressed diagnostics here? This is how the // code has always worked, but it isn't clear if that is correct. diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/NonLocalDocumentDiagnosticSource.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/NonLocalDocumentDiagnosticSource.cs index dbb26f218e7..bbf159bb0bc 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/NonLocalDocumentDiagnosticSource.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/NonLocalDocumentDiagnosticSource.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; internal sealed class NonLocalDocumentDiagnosticSource( - TextDocument document, Func? shouldIncludeAnalyzer) + TextDocument document, AnalyzerFilter analyzerFilter) : AbstractDocumentDiagnosticSource(document) { public override async Task> GetDiagnosticsAsync( @@ -22,10 +22,9 @@ public override async Task> GetDiagnosticsAsync( // document including those reported as a compilation end diagnostic. These are not included in document pull // (uses GetDiagnosticsForSpan) due to cost. var service = this.Solution.Services.GetRequiredService(); - var filter = service.GetDefaultAnalyzerFilter(Document.Project, diagnosticIds: null, shouldIncludeAnalyzer); var diagnostics = await service.GetDiagnosticsForIdsAsync( - Document.Project, [Document.Id], diagnosticIds: null, filter, includeLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false); + Document.Project, [Document.Id], diagnosticIds: null, analyzerFilter, includeLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false); // TODO(cyrusn): In the future we could consider reporting these, but with a flag on the diagnostic mentioning // that it is suppressed and should be hidden from the task list by default. diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/Public/PublicDocumentNonLocalDiagnosticSourceProvider.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/Public/PublicDocumentNonLocalDiagnosticSourceProvider.cs index dd616a40510..aac612e5d8f 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/Public/PublicDocumentNonLocalDiagnosticSourceProvider.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/Public/PublicDocumentNonLocalDiagnosticSourceProvider.cs @@ -33,8 +33,8 @@ public ValueTask> CreateDiagnosticSourcesAsync // Non-local document diagnostics are reported only when full solution analysis is enabled for analyzer execution. if (globalOptions.GetBackgroundAnalysisScope(context.GetRequiredDocument().Project.Language) == BackgroundAnalysisScope.FullSolution) { - // NOTE: Compiler does not report any non-local diagnostics, so we bail out for compiler analyzer. - return new([new NonLocalDocumentDiagnosticSource(context.GetRequiredDocument(), a => !a.IsCompilerAnalyzer())]); + // NOTE: Compiler does not report any non-local diagnostics, so we only ask to run non-compiler-analyzers. + return new([new NonLocalDocumentDiagnosticSource(context.GetRequiredDocument(), AnalyzerFilter.NonCompilerAnalyzer)]); } return new([]); diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/DocumentChanges/DidChangeHandler.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/DocumentChanges/DidChangeHandler.cs index 41241fe324d..f20d2f77124 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/DocumentChanges/DidChangeHandler.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/DocumentChanges/DidChangeHandler.cs @@ -28,11 +28,11 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(DidChangeTextDocumentPar public Task HandleRequestAsync(DidChangeTextDocumentParams request, RequestContext context, CancellationToken cancellationToken) { - var text = context.GetTrackedDocumentSourceText(request.TextDocument.DocumentUri); + var text = context.GetTrackedDocumentInfo(request.TextDocument.DocumentUri).SourceText; text = GetUpdatedSourceText(request.ContentChanges, text); - context.UpdateTrackedDocument(request.TextDocument.DocumentUri, text); + context.UpdateTrackedDocument(request.TextDocument.DocumentUri, text, request.TextDocument.Version); return SpecializedTasks.Default(); } diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs index 16c05505d5b..14054ddee9c 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs @@ -35,6 +35,6 @@ public async Task HandleNotificationAsync(LSP.DidOpenTextDocumentParams request, // Create SourceText from binary representation of the document, retrieve encoding from the request and checksum algorithm from the project. var sourceText = SourceText.From(request.TextDocument.Text, System.Text.Encoding.UTF8, SourceHashAlgorithms.OpenDocumentChecksumAlgorithm); - await context.StartTrackingAsync(request.TextDocument.DocumentUri, sourceText, request.TextDocument.LanguageId, cancellationToken).ConfigureAwait(false); + await context.StartTrackingAsync(request.TextDocument.DocumentUri, sourceText, request.TextDocument.LanguageId, request.TextDocument.Version, cancellationToken).ConfigureAwait(false); } } diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs index 1e06d5420aa..2237fb10798 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs @@ -3,6 +3,8 @@ // 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.Threading; @@ -12,6 +14,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler; @@ -48,14 +51,27 @@ public FormatDocumentOnTypeHandler(IGlobalOptionService globalOptions) return []; } - var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); + var linePosition = ProtocolConversions.PositionToLinePosition(request.Position); + var position = await document.GetPositionFromLinePositionAsync(linePosition, cancellationToken).ConfigureAwait(false); var formattingService = document.Project.Services.GetRequiredService(); var documentSyntax = await ParsedDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); - - if (!formattingService.ShouldFormatOnTypedCharacter(documentSyntax, request.Character[0], position, cancellationToken)) + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + + // The formatting service expects that the position is inside the token span associated with the typed character, but + // in VSCode this is not always the case - the position the client gives us is not necessarily the position of the typed character. + // For example when typing characters, the client may automatically + // 1. for '\n' - the client inserts indentation (to get '\n ') + // 2. for '{' - the client inserts '}' (to get '{}') + // + // When the formatter calls root.FindToken, it may return a token different from what triggered the on type formatting, which causes us + // to format way more than we want, depending on exactly where the position ends up. + // Here we do our best to adjust the position back to the typed char location. + if (text[position - 1] != request.Character[0]) { - return []; + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var adjustedToken = root.FindTokenOnLeftOfPosition(position); + position = adjustedToken.Span.End; } // We should use the options passed in by LSP instead of the document's options. @@ -65,41 +81,15 @@ public FormatDocumentOnTypeHandler(IGlobalOptionService globalOptions) AutoFormattingOptions = _globalOptions.GetAutoFormattingOptions(document.Project.Language) }; - var textChanges = formattingService.GetFormattingChangesOnTypedCharacter(documentSyntax, position, indentationOptions, cancellationToken); - if (textChanges.IsEmpty) + if (!formattingService.ShouldFormatOnTypedCharacter(documentSyntax, request.Character[0], position, cancellationToken)) { return []; } - if (SyntaxFacts.IsNewLine(request.Character[0])) + var textChanges = formattingService.GetFormattingChangesOnTypedCharacter(documentSyntax, position, indentationOptions, cancellationToken); + if (textChanges.IsEmpty) { - // When formatting after a newline is pressed, the cursor line will be all whitespace - // and we do not want to remove the indentation from it. - // - // Take the following example of pressing enter after an opening brace. - // - // ``` - // public void M() {||} - // ``` - // - // The editor moves the cursor to the next line and uses it's languageconfig to add - // the appropriate level of indentation. - // - // ``` - // public void M() { - // || - // } - // ``` - // - // At this point `formatOnType` is called. The formatting service will generate two - // text changes. The first moves the opening brace to a new line with proper - // indentation. The second removes the whitespace from the cursor line and rewrites - // the indentation prior to the closing brace. - // - // Letting the second change go through would be a bad experience for the user as they - // will now be responsible for adding back the proper indentation. - - textChanges = textChanges.WhereAsArray(static (change, position) => !change.Span.Contains(position), position); + return []; } return [.. textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, documentSyntax.Text))]; diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/IDocumentChangeTracker.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/IDocumentChangeTracker.cs index 33f71a36000..bce1d95bb73 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/IDocumentChangeTracker.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/IDocumentChangeTracker.cs @@ -17,14 +17,14 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler; /// internal interface IDocumentChangeTracker { - ValueTask StartTrackingAsync(DocumentUri documentUri, SourceText initialText, string languageId, CancellationToken cancellationToken); - void UpdateTrackedDocument(DocumentUri documentUri, SourceText text); + ValueTask StartTrackingAsync(DocumentUri documentUri, SourceText initialText, string languageId, int lspVersion, CancellationToken cancellationToken); + void UpdateTrackedDocument(DocumentUri documentUri, SourceText text, int lspVersion); ValueTask StopTrackingAsync(DocumentUri documentUri, CancellationToken cancellationToken); } internal sealed class NonMutatingDocumentChangeTracker : IDocumentChangeTracker { - public ValueTask StartTrackingAsync(DocumentUri documentUri, SourceText initialText, string languageId, CancellationToken cancellationToken) + public ValueTask StartTrackingAsync(DocumentUri documentUri, SourceText initialText, string languageId, int lspVersion, CancellationToken cancellationToken) { throw new InvalidOperationException("Mutating documents not allowed in a non-mutating request handler"); } @@ -34,7 +34,7 @@ public ValueTask StopTrackingAsync(DocumentUri documentUri, CancellationToken ca throw new InvalidOperationException("Mutating documents not allowed in a non-mutating request handler"); } - public void UpdateTrackedDocument(DocumentUri documentUri, SourceText text) + public void UpdateTrackedDocument(DocumentUri documentUri, SourceText text, int lspVersion) { throw new InvalidOperationException("Mutating documents not allowed in a non-mutating request handler"); } diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/InlineCompletions/XmlSnippetParser.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/InlineCompletions/XmlSnippetParser.cs index 0ca5cd43d7e..cb5db1ae695 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/InlineCompletions/XmlSnippetParser.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/InlineCompletions/XmlSnippetParser.cs @@ -100,7 +100,7 @@ private static CodeSnippet RetrieveSnippetXmlFromFile(SnippetInfo snippetInfo) return snippet; } - internal TestAccessor GetTestAccessor() => new TestAccessor(this); + internal TestAccessor GetTestAccessor() => new(this); internal readonly struct TestAccessor { diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs index cd6c950489a..d46f2b9f3c5 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs @@ -57,7 +57,12 @@ internal sealed class OnAutoInsertHandler( var servicesForDocument = _braceCompletionServices.SelectAsArray(s => s.Metadata.Language == document.Project.Language, s => s.Value); var isRazorRequest = context.ServerKind == WellKnownLspServerKinds.RazorLspServer; var position = ProtocolConversions.PositionToLinePosition(request.Position); - return GetOnAutoInsertResponseAsync(_globalOptions, servicesForDocument, document, position, request.Character, request.Options, isRazorRequest, cancellationToken); + var supportsVSExtensions = context.GetRequiredClientCapabilities().HasVisualStudioLspCapability(); + + // We want adjust the braces after enter for razor and non-VS clients. + // We don't do this via on type formatting as it does not support snippets. + var includeNewLineBraceFormatting = isRazorRequest || !supportsVSExtensions; + return GetOnAutoInsertResponseAsync(_globalOptions, servicesForDocument, document, position, request.Character, request.Options, includeNewLineBraceFormatting, cancellationToken); } internal static async Task GetOnAutoInsertResponseAsync( @@ -67,7 +72,7 @@ internal sealed class OnAutoInsertHandler( LinePosition linePosition, string character, LSP.FormattingOptions lspFormattingOptions, - bool isRazorRequest, + bool includeNewLineBraceFormatting, CancellationToken cancellationToken) { var service = document.GetRequiredLanguageService(); @@ -92,7 +97,8 @@ internal sealed class OnAutoInsertHandler( // Only support this for razor as LSP doesn't support overtype yet. // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1165179/ // Once LSP supports overtype we can move all of brace completion to LSP. - if (character == "\n" && isRazorRequest) + + if (character == "\n" && includeNewLineBraceFormatting) { var indentationOptions = new IndentationOptions(formattingOptions) { @@ -125,7 +131,7 @@ internal sealed class OnAutoInsertHandler( var result = character == "\n" ? service.GetDocumentationCommentSnippetOnEnterTyped(parsedDocument, position, options, cancellationToken) - : service.GetDocumentationCommentSnippetOnCharacterTyped(parsedDocument, position, options, cancellationToken, addIndentation: false); + : service.GetDocumentationCommentSnippetOnCharacterTyped(parsedDocument, position, options, cancellationToken, addIndentation: true); if (result == null) return null; @@ -194,6 +200,7 @@ internal sealed class OnAutoInsertHandler( var textChange = await GetCollapsedChangeAsync(textChanges, document, cancellationToken).ConfigureAwait(false); var newText = GetTextChangeTextWithCaretAtLocation(newSourceText, textChange, desiredCaretLinePosition); + var autoInsertChange = new LSP.VSInternalDocumentOnAutoInsertResponseItem { TextEditFormat = LSP.InsertTextFormat.Snippet, diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/ProjectContext/GetTextDocumentWithContextHandler.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/ProjectContext/GetTextDocumentWithContextHandler.cs index 0f718729323..62fb88c9768 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/ProjectContext/GetTextDocumentWithContextHandler.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/ProjectContext/GetTextDocumentWithContextHandler.cs @@ -24,7 +24,7 @@ internal class GetTextDocumentWithContextHandler() : ILspServiceDocumentRequestH public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public TextDocumentIdentifier GetTextDocumentIdentifier(VSGetProjectContextsParams request) => new TextDocumentIdentifier { DocumentUri = request.TextDocument.DocumentUri }; + public TextDocumentIdentifier GetTextDocumentIdentifier(VSGetProjectContextsParams request) => new() { DocumentUri = request.TextDocument.DocumentUri }; public Task HandleRequestAsync(VSGetProjectContextsParams request, RequestContext context, CancellationToken cancellationToken) { diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/RequestContext.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/RequestContext.cs index c5639e2e425..02ffa3696df 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/RequestContext.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/RequestContext.cs @@ -42,7 +42,7 @@ internal readonly struct RequestContext /// It contains text that is consistent with all prior LSP text sync notifications, but LSP text sync requests /// which are ordered after this one in the queue are not reflected here. /// - private readonly ImmutableDictionary _trackedDocuments; + private readonly ImmutableDictionary _trackedDocuments; private readonly ILspServices _lspServices; @@ -171,7 +171,7 @@ public RequestContext( WellKnownLspServerKinds serverKind, TextDocument? document, IDocumentChangeTracker documentChangeTracker, - ImmutableDictionary trackedDocuments, + ImmutableDictionary trackedDocuments, ImmutableArray supportedLanguages, ILspServices lspServices, CancellationToken queueCancellationToken) @@ -299,20 +299,20 @@ public static async Task CreateAsync( /// Allows a mutating request to open a document and start it being tracked. /// Mutating requests are serialized by the execution queue in order to prevent concurrent access. /// - public ValueTask StartTrackingAsync(DocumentUri uri, SourceText initialText, string languageId, CancellationToken cancellationToken) - => _documentChangeTracker.StartTrackingAsync(uri, initialText, languageId, cancellationToken); + public ValueTask StartTrackingAsync(DocumentUri uri, SourceText initialText, string languageId, int lspVersion, CancellationToken cancellationToken) + => _documentChangeTracker.StartTrackingAsync(uri, initialText, languageId, lspVersion, cancellationToken); /// /// Allows a mutating request to update the contents of a tracked document. /// Mutating requests are serialized by the execution queue in order to prevent concurrent access. /// - public void UpdateTrackedDocument(DocumentUri uri, SourceText changedText) - => _documentChangeTracker.UpdateTrackedDocument(uri, changedText); + public void UpdateTrackedDocument(DocumentUri uri, SourceText changedText, int lspVersion) + => _documentChangeTracker.UpdateTrackedDocument(uri, changedText, lspVersion); - public SourceText GetTrackedDocumentSourceText(DocumentUri documentUri) + public TrackedDocumentInfo GetTrackedDocumentInfo(DocumentUri documentUri) { Contract.ThrowIfFalse(_trackedDocuments.ContainsKey(documentUri), $"Attempted to get text for {documentUri} which is not open."); - return _trackedDocuments[documentUri].Text; + return _trackedDocuments[documentUri]; } /// diff --git a/src/roslyn/src/LanguageServer/Protocol/LanguageInfoProvider.cs b/src/roslyn/src/LanguageServer/Protocol/LanguageInfoProvider.cs index ef4b6a5db2c..dcd3db3cde0 100644 --- a/src/roslyn/src/LanguageServer/Protocol/LanguageInfoProvider.cs +++ b/src/roslyn/src/LanguageServer/Protocol/LanguageInfoProvider.cs @@ -18,7 +18,7 @@ internal sealed class LanguageInfoProvider : ILanguageInfoProvider private static readonly LanguageInformation s_csharpLanguageInformation = new(LanguageNames.CSharp, ".csx"); private static readonly LanguageInformation s_fsharpLanguageInformation = new(LanguageNames.FSharp, ".fsx"); private static readonly LanguageInformation s_vbLanguageInformation = new(LanguageNames.VisualBasic, ".vbx"); - private static readonly LanguageInformation s_typeScriptLanguageInformation = new LanguageInformation(InternalLanguageNames.TypeScript, scriptExtension: null); + private static readonly LanguageInformation s_typeScriptLanguageInformation = new(InternalLanguageNames.TypeScript, scriptExtension: null); private static readonly LanguageInformation s_razorLanguageInformation = new(RazorLanguageName, scriptExtension: null); private static readonly LanguageInformation s_xamlLanguageInformation = new("XAML", scriptExtension: null); diff --git a/src/roslyn/src/LanguageServer/Protocol/NoOpLspLogger.cs b/src/roslyn/src/LanguageServer/Protocol/NoOpLspLogger.cs index 13b951a853f..1b76529a669 100644 --- a/src/roslyn/src/LanguageServer/Protocol/NoOpLspLogger.cs +++ b/src/roslyn/src/LanguageServer/Protocol/NoOpLspLogger.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer; internal sealed class NoOpLspLogger : AbstractLspLogger, ILspService { - public static readonly NoOpLspLogger Instance = new NoOpLspLogger(); + public static readonly NoOpLspLogger Instance = new(); private NoOpLspLogger() { } diff --git a/src/roslyn/src/LanguageServer/Protocol/Protocol/Converters/SumConverter.cs b/src/roslyn/src/LanguageServer/Protocol/Protocol/Converters/SumConverter.cs index 8c766764a6f..a2837a9a0e1 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Protocol/Converters/SumConverter.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Protocol/Converters/SumConverter.cs @@ -182,7 +182,7 @@ public UnionTypeInfo(Type type, ConstructorInfo constructor, KindAttribute? kind internal sealed class SumConverter : JsonConverter where T : struct, ISumType { - private static readonly ConcurrentDictionary SumTypeCache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary SumTypeCache = new(); /// public override T Read(ref Utf8JsonReader reader, Type objectType, JsonSerializerOptions options) diff --git a/src/roslyn/src/LanguageServer/Protocol/Protocol/Extensions/VSMethods.cs b/src/roslyn/src/LanguageServer/Protocol/Protocol/Extensions/VSMethods.cs index 2c36060f63d..0345451d051 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Protocol/Extensions/VSMethods.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Protocol/Extensions/VSMethods.cs @@ -23,5 +23,5 @@ internal static class VSMethods /// /// Strongly typed request object for 'textDocument/_vs_getProjectContexts'. /// - public static readonly LspRequest GetProjectContexts = new LspRequest(GetProjectContextsName); + public static readonly LspRequest GetProjectContexts = new(GetProjectContextsName); } diff --git a/src/roslyn/src/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs b/src/roslyn/src/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs index 09e0bb5b763..2708b28c4e3 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs @@ -14,7 +14,7 @@ namespace Roslyn.LanguageServer.Protocol; internal sealed class OptimizedVSCompletionListJsonConverter : JsonConverter { public static readonly OptimizedVSCompletionListJsonConverter Instance = new(); - private static readonly ConcurrentDictionary IconRawJson = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary IconRawJson = new(); public override OptimizedVSCompletionList Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException(); diff --git a/src/roslyn/src/LanguageServer/Protocol/Protocol/Internal/VSInternalMethods.cs b/src/roslyn/src/LanguageServer/Protocol/Protocol/Internal/VSInternalMethods.cs index d3ac3b36659..05c4a21bca6 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Protocol/Internal/VSInternalMethods.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Protocol/Internal/VSInternalMethods.cs @@ -28,52 +28,52 @@ internal static class VSInternalMethods /// /// Strongly typed message object for 'textDocument/_vs_onAutoInsert'. /// - public static readonly LspRequest OnAutoInsert = new LspRequest(OnAutoInsertName); + public static readonly LspRequest OnAutoInsert = new(OnAutoInsertName); /// /// Strongly typed message object for 'textDocument/_vs_iconMappingResolve'. /// - public static readonly LspRequest TextDocumentIconMappingResolve = new LspRequest(TextDocumentIconMappingResolveName); + public static readonly LspRequest TextDocumentIconMappingResolve = new(TextDocumentIconMappingResolveName); /// /// Strongly typed message object for 'textDocument/_vs_diagnostic'. /// - public static readonly LspRequest DocumentPullDiagnostic = new LspRequest(DocumentPullDiagnosticName); + public static readonly LspRequest DocumentPullDiagnostic = new(DocumentPullDiagnosticName); /// /// Strongly typed message object for 'workspace/_vs_diagnostic'. /// - public static readonly LspRequest WorkspacePullDiagnostic = new LspRequest(WorkspacePullDiagnosticName); + public static readonly LspRequest WorkspacePullDiagnostic = new(WorkspacePullDiagnosticName); /// /// Strongly typed message object for 'textDocument/_vs_validateBreakableRange'. /// - public static readonly LspRequest TextDocumentValidateBreakableRange = new LspRequest(TextDocumentValidateBreakableRangeName); + public static readonly LspRequest TextDocumentValidateBreakableRange = new(TextDocumentValidateBreakableRangeName); /// /// Strongly typed message object for 'textDocument/inlineCompletion'. /// - public static readonly LspRequest TextDocumentInlineCompletion = new LspRequest(TextDocumentInlineCompletionName); + public static readonly LspRequest TextDocumentInlineCompletion = new(TextDocumentInlineCompletionName); /// /// Strongly typed message object for 'textDocument/_vs_uriPresentation'. /// - public static readonly LspRequest TextDocumentUriPresentation = new LspRequest(TextDocumentUriPresentationName); + public static readonly LspRequest TextDocumentUriPresentation = new(TextDocumentUriPresentationName); /// /// Strongly typed message object for 'textDocument/_vs_textPresentation'. /// - public static readonly LspRequest TextDocumentTextPresentation = new LspRequest(TextDocumentTextPresentationName); + public static readonly LspRequest TextDocumentTextPresentation = new(TextDocumentTextPresentationName); /// /// Strongly typed message object for 'textDocument/_vs_spellCheckableRanges'. /// - public static readonly LspRequest TextDocumentSpellCheckableRanges = new LspRequest(TextDocumentSpellCheckableRangesName); + public static readonly LspRequest TextDocumentSpellCheckableRanges = new(TextDocumentSpellCheckableRangesName); /// /// Strongly typed message object for 'workspace/_vs_spellCheckableRanges'. /// - public static readonly LspRequest WorkspaceSpellCheckableRanges = new LspRequest(WorkspaceSpellCheckableRangesName); + public static readonly LspRequest WorkspaceSpellCheckableRanges = new(WorkspaceSpellCheckableRangesName); /// /// Strongly typed message object for 'workspace/_vs_mapCode' diff --git a/src/roslyn/src/LanguageServer/Protocol/Protocol/SumType.cs b/src/roslyn/src/LanguageServer/Protocol/Protocol/SumType.cs index 1dc5b878faa..c41306f918f 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Protocol/SumType.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Protocol/SumType.cs @@ -62,7 +62,7 @@ public SumType(T2 val) /// Implicitly wraps a value of type with a . /// /// Value to be wrapped. - public static implicit operator SumType(T1 val) => new SumType(val); + public static implicit operator SumType(T1 val) => new(val); /// /// Implicitly wraps a value of type with a . @@ -74,7 +74,7 @@ public SumType(T2 val) /// Implicitly wraps a value of type with a . /// /// Value to be wrapped. - public static implicit operator SumType(T2 val) => new SumType(val); + public static implicit operator SumType(T2 val) => new(val); /// /// Implicitly wraps a value of type with a . @@ -270,7 +270,7 @@ public SumType(T3 val) /// Implicitly wraps a value of type with a . /// /// Value to be wrap. - public static implicit operator SumType(T1 val) => new SumType(val); + public static implicit operator SumType(T1 val) => new(val); /// /// Implicitly wraps a value of type with a . @@ -282,7 +282,7 @@ public SumType(T3 val) /// Implicitly wraps a value of type with a . /// /// Value to be wrap. - public static implicit operator SumType(T2 val) => new SumType(val); + public static implicit operator SumType(T2 val) => new(val); /// /// Implicitly wraps a value of type with a . @@ -294,7 +294,7 @@ public SumType(T3 val) /// Implicitly wraps a value of type with a . /// /// Value to be wrap. - public static implicit operator SumType(T3 val) => new SumType(val); + public static implicit operator SumType(T3 val) => new(val); /// /// Implicitly wraps a value of type with a . @@ -570,7 +570,7 @@ public SumType(T4 val) /// Implicitly wraps a value of type with a . /// /// Value to be wrap. - public static implicit operator SumType(T1 val) => new SumType(val); + public static implicit operator SumType(T1 val) => new(val); /// /// Implicitly wraps a value of type with a . @@ -582,7 +582,7 @@ public SumType(T4 val) /// Implicitly wraps a value of type with a . /// /// Value to be wrap. - public static implicit operator SumType(T2 val) => new SumType(val); + public static implicit operator SumType(T2 val) => new(val); /// /// Implicitly wraps a value of type with a . @@ -594,7 +594,7 @@ public SumType(T4 val) /// Implicitly wraps a value of type with a . /// /// Value to be wrap. - public static implicit operator SumType(T3 val) => new SumType(val); + public static implicit operator SumType(T3 val) => new(val); /// /// Implicitly wraps a value of type with a . @@ -606,7 +606,7 @@ public SumType(T4 val) /// Implicitly wraps a value of type with a . /// /// Value to be wrap. - public static implicit operator SumType(T4 val) => new SumType(val); + public static implicit operator SumType(T4 val) => new(val); /// /// Implicitly wraps a value of type with a . diff --git a/src/roslyn/src/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs b/src/roslyn/src/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs index 832fb857a15..329d1f15c6b 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs @@ -62,7 +62,7 @@ internal sealed class LspWorkspaceManager : IDocumentChangeTracker, ILspService /// the URI. /// Access to this is guaranteed to be serial by the /// - private ImmutableDictionary _trackedDocuments = ImmutableDictionary.Empty; + private ImmutableDictionary _trackedDocuments = ImmutableDictionary.Empty; private readonly ILspLogger _logger; private readonly ILspMiscellaneousFilesWorkspaceProvider? _lspMiscellaneousFilesWorkspaceProvider; @@ -103,7 +103,7 @@ private static async ValueTask ApplyChangeToMutatingWorkspaceAsync(Workspace wor /// /// is true which means this runs serially in the /// - public async ValueTask StartTrackingAsync(DocumentUri uri, SourceText documentText, string languageId, CancellationToken cancellationToken) + public async ValueTask StartTrackingAsync(DocumentUri uri, SourceText documentText, string languageId, int lspVersion, CancellationToken cancellationToken) { // First, store the LSP view of the text as the uri is now owned by the LSP client. Contract.ThrowIfTrue(_trackedDocuments.ContainsKey(uri), $"didOpen received for {uri} which is already open."); @@ -113,7 +113,7 @@ public async ValueTask StartTrackingAsync(DocumentUri uri, SourceText documentTe _logger.LogError($"Unable to parse URI {uri}"); } - _trackedDocuments = _trackedDocuments.Add(uri, (documentText, languageId)); + _trackedDocuments = _trackedDocuments.Add(uri, new(documentText, languageId, lspVersion)); // If LSP changed, we need to compare against the workspace again to get the updated solution. _cachedLspSolutions.Clear(); @@ -197,12 +197,12 @@ await ApplyChangeToMutatingWorkspaceAsync(workspace, uri, async (_, documentId) /// /// is true which means this runs serially in the /// - public void UpdateTrackedDocument(DocumentUri uri, SourceText newSourceText) + public void UpdateTrackedDocument(DocumentUri uri, SourceText newSourceText, int lspVersion) { // Store the updated LSP view of the source text. Contract.ThrowIfFalse(_trackedDocuments.ContainsKey(uri), $"didChange received for {uri} which is not open."); - var (_, language) = _trackedDocuments[uri]; - _trackedDocuments = _trackedDocuments.SetItem(uri, (newSourceText, language)); + var (_, language, _) = _trackedDocuments[uri]; + _trackedDocuments = _trackedDocuments.SetItem(uri, new(newSourceText, language, lspVersion)); // If LSP changed, we need to compare against the workspace again to get the updated solution. _cachedLspSolutions.Clear(); @@ -210,7 +210,7 @@ public void UpdateTrackedDocument(DocumentUri uri, SourceText newSourceText) LspTextChanged?.Invoke(this, EventArgs.Empty); } - public ImmutableDictionary GetTrackedLspText() => _trackedDocuments; + public ImmutableDictionary GetTrackedLspText() => _trackedDocuments; #endregion @@ -285,7 +285,7 @@ public void UpdateTrackedDocument(DocumentUri uri, SourceText newSourceText) var workspaceKind = document.Project.Solution.WorkspaceKind; _requestTelemetryLogger.UpdateFindDocumentTelemetryData(success: true, workspaceKind); _requestTelemetryLogger.UpdateUsedForkedSolutionCounter(isForked); - _logger.LogDebug($"{document.FilePath} found in workspace {workspaceKind}"); + _logger.LogDebug($"{document.FilePath} found in workspace {workspaceKind}; project {document.Project.Name}"); return (workspace, document.Project.Solution, document); } @@ -302,7 +302,7 @@ public void UpdateTrackedDocument(DocumentUri uri, SourceText newSourceText) { try { - var miscDocument = await _lspMiscellaneousFilesWorkspaceProvider.AddMiscellaneousDocumentAsync(uri, trackedDocument.Text, trackedDocument.LanguageId, _logger).ConfigureAwait(false); + var miscDocument = await _lspMiscellaneousFilesWorkspaceProvider.AddMiscellaneousDocumentAsync(uri, trackedDocument.SourceText, trackedDocument.LanguageId, _logger).ConfigureAwait(false); if (miscDocument is not null) return (miscDocument.Project.Solution.Workspace, miscDocument.Project.Solution, miscDocument); } @@ -391,10 +391,10 @@ .. registeredWorkspaces.Where(workspace => workspace.Kind == WorkspaceKind.Misce var sourceGeneratedDocuments = _trackedDocuments.Keys.Where(static trackedDocument => trackedDocument.ParsedUri?.Scheme == SourceGeneratedDocumentUri.Scheme) // We know we have a non null URI with a source generated scheme. - .Select(uri => (identity: SourceGeneratedDocumentUri.DeserializeIdentity(workspaceCurrentSolution, uri.ParsedUri!), _trackedDocuments[uri].Text)) + .Select(uri => (identity: SourceGeneratedDocumentUri.DeserializeIdentity(workspaceCurrentSolution, uri.ParsedUri!), _trackedDocuments[uri].SourceText)) .SelectAsArray( predicate: tuple => tuple.identity.HasValue, - selector: tuple => (tuple.identity!.Value, DateTime.Now, tuple.Text)); + selector: tuple => (tuple.identity!.Value, DateTime.Now, tuple.SourceText)); // First we check if normal document text matches the workspace solution. // This does not look at source generated documents. @@ -428,7 +428,7 @@ .. registeredWorkspaces.Where(workspace => workspace.Kind == WorkspaceKind.Misce if (!doesAllTextMatch) { foreach (var (uri, workspaceDocuments) in documentsInWorkspace) - lspSolution = lspSolution.WithDocumentText(workspaceDocuments.Select(d => d.Id), _trackedDocuments[uri].Text); + lspSolution = lspSolution.WithDocumentText(workspaceDocuments.Select(d => d.Id), _trackedDocuments[uri].SourceText); } // If the source generated documents matched we can leave the source generated documents as-is @@ -444,7 +444,7 @@ .. registeredWorkspaces.Where(workspace => workspace.Kind == WorkspaceKind.Misce async ValueTask TryOpenAndEditDocumentsInMutatingWorkspaceAsync(Workspace workspace) { - foreach (var (uri, (sourceText, _)) in _trackedDocuments) + foreach (var (uri, (sourceText, _, _)) in _trackedDocuments) { await ApplyChangeToMutatingWorkspaceAsync(workspace, uri, async (mutatingWorkspace, documentId) => { @@ -512,7 +512,7 @@ private async Task DoesAllTextMatchWorkspaceSolutionAsync(ImmutableDiction { // We're comparing text, so we can take any of the linked documents. var firstDocument = documentsForUri.First(); - var isTextEquivalent = await AreChecksumsEqualAsync(firstDocument, _trackedDocuments[uriInWorkspace].Text, cancellationToken).ConfigureAwait(false); + var isTextEquivalent = await AreChecksumsEqualAsync(firstDocument, _trackedDocuments[uriInWorkspace].SourceText, cancellationToken).ConfigureAwait(false); if (!isTextEquivalent) { diff --git a/src/roslyn/src/LanguageServer/Protocol/Workspaces/TrackedDocumentInfo.cs b/src/roslyn/src/LanguageServer/Protocol/Workspaces/TrackedDocumentInfo.cs new file mode 100644 index 00000000000..ff9d2a2b86a --- /dev/null +++ b/src/roslyn/src/LanguageServer/Protocol/Workspaces/TrackedDocumentInfo.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 Microsoft.CodeAnalysis.Text; +using Roslyn.LanguageServer.Protocol; + +namespace Microsoft.CodeAnalysis.LanguageServer; + +/// +/// Holds state information from the client about a tracked LSP document. See +/// +/// A snapshot of the text as seen by LSP. +/// The language id for the text, from +/// The client version associated with the text, from +internal record struct TrackedDocumentInfo(SourceText SourceText, string LanguageId, int LspVersion); diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs index b2d1efa5074..98a57c91210 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs @@ -521,7 +521,7 @@ class BCD } private static LSP.TextEdit GenerateTextEdit(string newText, LSP.Range range) - => new LSP.TextEdit + => new() { NewText = newText, Range = range @@ -530,11 +530,11 @@ private static LSP.TextEdit GenerateTextEdit(string newText, LSP.Range range) private static WorkspaceEdit GenerateWorkspaceEdit( IList locations, SumType[] edits) - => new LSP.WorkspaceEdit + => new() { DocumentChanges = new TextDocumentEdit[] { - new TextDocumentEdit + new() { TextDocument = new OptionalVersionedTextDocumentIdentifier { diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs index 6f38e8f3557..20f0f483ebb 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs @@ -325,7 +325,7 @@ private static async Task RunGetCodeActionResolveAsync( } internal static CodeActionParams CreateCodeActionParams(LSP.Location caret) - => new CodeActionParams + => new() { TextDocument = CreateTextDocumentIdentifier(caret.DocumentUri), Range = caret.Range, diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs index 82ac970ee9e..f3cb0980f92 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs @@ -184,7 +184,7 @@ private static TestDiagnosticResult ConvertWorkspaceDiagnosticResult(SumType CreateDiagnosticParamsFromPreviousReports(ImmutableArray results) { // If there was no resultId provided in the response, we cannot create previous results for it. - return [.. results.Where(r => r.ResultId != null).Select(r => (r.ResultId!, r.TextDocument))]; + return results.SelectAsArray(r => r.ResultId != null, r => (r.ResultId!, r.TextDocument)); } private protected static VSInternalDocumentDiagnosticsParams CreateDocumentDiagnosticParams( @@ -371,8 +371,8 @@ private protected sealed record TestDiagnosticResult(TextDocumentIdentifier Text [DiagnosticAnalyzer(InternalLanguageNames.TypeScript), PartNotDiscoverable] private sealed class MockTypescriptDiagnosticAnalyzer : DocumentDiagnosticAnalyzer { - public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor( - "TS01", "TS error", "TS error", "Error", DiagnosticSeverity.Error, isEnabledByDefault: true); + public static readonly DiagnosticDescriptor Descriptor = new( + "TS01", "TS error", "TS error", "Error", DiagnosticSeverity.Error, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics => [Descriptor]; diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs index 35adfcd8398..da3f4b8f745 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs @@ -246,7 +246,7 @@ public Task> GetDiagnosticsAsync(RequestContext c return Task.FromResult>([DiagnosticData.Create(diagnostic, context.TextDocument.Project)]); } - public LSP.TextDocumentIdentifier? GetDocumentIdentifier() => new LSP.TextDocumentIdentifier + public LSP.TextDocumentIdentifier? GetDocumentIdentifier() => new() { DocumentUri = textDocument.GetURI() }; diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.cs index 364dec55b72..edb76dd49fa 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.cs @@ -2,9 +2,16 @@ // 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.Composition; using System.Linq; +using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.Handler.DocumentChanges; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CommonLanguageServerProtocol.Framework; using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; using Xunit; @@ -13,11 +20,9 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.DocumentChanges; -public sealed partial class DocumentChangesTests : AbstractLanguageServerProtocolTests +public sealed partial class DocumentChangesTests(ITestOutputHelper testOutputHelper) : AbstractLanguageServerProtocolTests(testOutputHelper) { - public DocumentChangesTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) - { - } + protected override TestComposition Composition => base.Composition.AddParts(typeof(TestVersionHandler)); [Theory, CombinatorialData] public async Task DocumentChanges_EndToEnd(bool mutatingLspWorkspace) @@ -425,6 +430,33 @@ void M() } } + [Theory, CombinatorialData] + public async Task DocumentChanges_WithVersion(bool mutatingLspWorkspace) + { + await using var testLspServer = await CreateTestLspServerAsync("Hello{|type:|}", mutatingLspWorkspace, CapabilitiesWithVSExtensions); + Assert.Empty(testLspServer.GetTrackedTexts()); + + var locationTyped = testLspServer.GetLocations("type").Single(); + + await DidOpen(testLspServer, locationTyped.DocumentUri, version: 0); + var version = await GetVersionAsync(locationTyped.DocumentUri); + Assert.Equal(0, version); + + await DidChange(testLspServer, locationTyped.DocumentUri, version: 1, (0, 5, ", World")); + Assert.Equal(1, await GetVersionAsync(locationTyped.DocumentUri)); + + var document = testLspServer.GetTrackedTexts().FirstOrDefault(); + Assert.Equal("Hello, World", document!.ToString()); + + async Task GetVersionAsync(DocumentUri documentUri) + { + var textDocumentIdentifier = new LSP.TextDocumentIdentifier() { DocumentUri = documentUri }; + var response = await testLspServer.ExecuteRequestAsync(TestVersionHandler.MethodName, textDocumentIdentifier, CancellationToken.None); + Assert.NotNull(response); + return response.Version; + } + } + private async Task<(TestLspServer, LSP.Location, string)> GetTestLspServerAndLocationAsync(string source, bool mutatingLspWorkspace) { var testLspServer = await CreateTestLspServerAsync(source, mutatingLspWorkspace, CapabilitiesWithVSExtensions); @@ -434,10 +466,39 @@ void M() return (testLspServer, locationTyped, documentText.ToString()); } - private static Task DidOpen(TestLspServer testLspServer, DocumentUri uri) => testLspServer.OpenDocumentAsync(uri); + private static Task DidOpen(TestLspServer testLspServer, DocumentUri uri, int version = 0) => testLspServer.OpenDocumentAsync(uri, version: version); - private static async Task DidChange(TestLspServer testLspServer, DocumentUri uri, params (int line, int column, string text)[] changes) - => await testLspServer.InsertTextAsync(uri, changes); + private static async Task DidChange(TestLspServer testLspServer, DocumentUri uri, int version, params (int line, int column, string text)[] changes) + => await testLspServer.InsertTextAsync(uri, version, changes); + + private static Task DidChange(TestLspServer testLspServer, DocumentUri uri, params (int line, int column, string text)[] changes) + => DidChange(testLspServer, uri, version: 0, changes); private static async Task DidClose(TestLspServer testLspServer, DocumentUri uri) => await testLspServer.CloseDocumentAsync(uri); + + internal record TestVersionResponse(int Version); + + [ExportCSharpVisualBasicStatelessLspService(typeof(TestVersionHandler)), PartNotDiscoverable, Shared] + [Method(MethodName)] + [method: ImportingConstructor] + [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + internal sealed class TestVersionHandler() : ILspServiceDocumentRequestHandler + { + public const string MethodName = nameof(TestVersionHandler); + + public bool MutatesSolutionState => false; + public bool RequiresLSPSolution => true; + + public TextDocumentIdentifier GetTextDocumentIdentifier(TextDocumentIdentifier request) + { + return request; + } + + public Task HandleRequestAsync(TextDocumentIdentifier request, RequestContext context, CancellationToken cancellationToken) + { + var trackedDocumentInfo = context.GetTrackedDocumentInfo(request.DocumentUri); + + return Task.FromResult(new TestVersionResponse(trackedDocumentInfo.LspVersion)); + } + } } diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs index 5b0de3bd09d..78e7aa8133d 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs @@ -90,7 +90,7 @@ private async Task AssertFoldingRanges(bool mutatingLspWorkspace, string markup, } private static LSP.FoldingRange CreateFoldingRange(string kind, LSP.Range range, string collapsedText) - => new LSP.FoldingRange() + => new() { Kind = kind switch { diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentOnTypeTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentOnTypeTests.cs index 9594294894b..d5d02f9f931 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentOnTypeTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentOnTypeTests.cs @@ -89,13 +89,76 @@ void M() { """; await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); var locationTyped = testLspServer.GetLocations("type").Single(); + await AssertFormatDocumentOnTypeAsync(testLspServer, "\n", locationTyped, """ + class A + { + void M() { + + } + } + """); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/vscode-csharp/issues/8429")] + public async Task TestFormatDocumentOnType_NewLineBeforeMultilineComment(bool mutatingLspWorkspace) + { + var markup = + """ + class A + { + void M() + { + } + {|type:|} + /* + private void Do() { } + */ + } + """; + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + var locationTyped = testLspServer.GetLocations("type").Single(); + await AssertFormatDocumentOnTypeAsync(testLspServer, "\n", locationTyped, """ + class A + { + void M() + { + } + + /* + private void Do() { } + */ + } + """); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/vscode-csharp/issues/8429")] + public async Task TestFormatDocumentOnType_NewLineBeforeMultilineComment2(bool mutatingLspWorkspace) + { + var markup = + """ + class A + { + void M() + { + } + {|type:|} + /* + private void Do() { } + */ + } + """; + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + var locationTyped = testLspServer.GetLocations("type").Single(); await AssertFormatDocumentOnTypeAsync(testLspServer, "\n", locationTyped, """ class A { void M() { - } + + /* + private void Do() { } + */ } """); } @@ -122,7 +185,7 @@ private static LSP.DocumentOnTypeFormattingParams CreateDocumentOnTypeFormatting LSP.Location locationTyped, bool insertSpaces, int tabSize) - => new LSP.DocumentOnTypeFormattingParams() + => new() { Position = locationTyped.Range.Start, Character = characterTyped, diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentRangeTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentRangeTests.cs index cd8b7470604..dcd108c0968 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentRangeTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentRangeTests.cs @@ -96,7 +96,7 @@ private static LSP.DocumentRangeFormattingParams CreateDocumentRangeFormattingPa LSP.Location location, bool insertSpaces, int tabSize) - => new LSP.DocumentRangeFormattingParams() + => new() { Range = location.Range, TextDocument = CreateTextDocumentIdentifier(location.DocumentUri), diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentTests.cs index 834a9909f24..0f3678e26c8 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentTests.cs @@ -316,7 +316,7 @@ private static async Task AssertFormatDocumentAsync( } private static LSP.DocumentFormattingParams CreateDocumentFormattingParams(DocumentUri uri, bool insertSpaces, int tabSize) - => new LSP.DocumentFormattingParams() + => new() { TextDocument = CreateTextDocumentIdentifier(uri), Options = new LSP.FormattingOptions() diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Highlights/DocumentHighlightTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Highlights/DocumentHighlightTests.cs index b935492779f..a2f7157944b 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Highlights/DocumentHighlightTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Highlights/DocumentHighlightTests.cs @@ -113,7 +113,7 @@ void M() } private static LSP.DocumentHighlight CreateDocumentHighlight(LSP.DocumentHighlightKind kind, LSP.Location location) - => new LSP.DocumentHighlight() + => new() { Kind = kind, Range = location.Range diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/MapCode/MapCodeTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/MapCode/MapCodeTests.cs index 8cd32466ff7..cf6ac808c2b 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/MapCode/MapCodeTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/MapCode/MapCodeTests.cs @@ -48,7 +48,7 @@ public MapCodeTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) } private static ClientCapabilities CreateClientCapabilities(bool supportDocumentChanges) - => new LSP.ClientCapabilities + => new() { Workspace = new LSP.WorkspaceClientCapabilities { diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs index 57d63821359..7ef6764930a 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.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.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -23,7 +24,7 @@ public OnAutoInsertTests(ITestOutputHelper testOutputHelper) : base(testOutputHe [Theory, CombinatorialData] public Task OnAutoInsert_CommentCharacter(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("/", """ + => VerifyCSharpMarkupAndExpected("/", """ class A { ///{|type:|} @@ -35,8 +36,8 @@ void M() class A { /// - /// $0 - /// + /// $0 + /// void M() { } @@ -45,7 +46,7 @@ void M() [Theory, CombinatorialData] public Task OnAutoInsert_CommentCharacter_WithComment(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("/", """ + => VerifyCSharpMarkupAndExpected("/", """ class A { ///{|type:|} This is an existing comment @@ -57,8 +58,8 @@ void M() class A { /// - /// $0This is an existing comment - /// + /// $0This is an existing comment + /// void M() { } @@ -67,7 +68,7 @@ void M() [Theory, CombinatorialData] public Task OnAutoInsert_CommentCharacter_WithComment_NoSpace(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("/", """ + => VerifyCSharpMarkupAndExpected("/", """ class A { ///{|type:|}This is an existing comment @@ -79,8 +80,8 @@ void M() class A { /// - /// $0This is an existing comment - /// + /// $0This is an existing comment + /// void M() { } @@ -89,7 +90,7 @@ void M() [Theory, CombinatorialData] public Task OnAutoInsert_CommentCharacter_VB(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("'", """ + => VerifyCSharpMarkupAndExpected("'", """ Class A '''{|type:|} Sub M() @@ -98,8 +99,8 @@ End Class """, """ Class A ''' - ''' $0 - ''' + ''' $0 + ''' Sub M() End Sub End Class @@ -107,7 +108,7 @@ End Class [Theory, CombinatorialData] public Task OnAutoInsert_ParametersAndReturns(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("/", """ + => VerifyCSharpMarkupAndExpected("/", """ class A { ///{|type:|} @@ -119,11 +120,11 @@ string M(int foo, bool bar) class A { /// - /// $0 - /// - /// - /// - /// + /// $0 + /// + /// + /// + /// string M(int foo, bool bar) { } @@ -156,7 +157,7 @@ void M() [Theory, CombinatorialData] public Task OnAutoInsert_EnterKey(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("\n", """ + => VerifyCSharpMarkupAndExpected("\n", """ class A { /// @@ -182,7 +183,7 @@ void M() [Theory, CombinatorialData] public Task OnAutoInsert_EnterKey2(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("\n", """ + => VerifyCSharpMarkupAndExpected("\n", """ class A { /// @@ -208,7 +209,7 @@ void M() [Theory, CombinatorialData] public Task OnAutoInsert_EnterKey3(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("\n", """ + => VerifyCSharpMarkupAndExpected("\n", """ class A { /// @@ -234,7 +235,7 @@ string M(int foo, bool bar) [Theory, CombinatorialData] public Task OnAutoInsert_BraceFormatting(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("\n", """ + => VerifyCSharpMarkupAndExpected("\n", """ class A { void M() {{|type:|} @@ -248,11 +249,11 @@ void M() $0 } } - """, mutatingLspWorkspace, serverKind: WellKnownLspServerKinds.RazorLspServer); + """, mutatingLspWorkspace, useVSCapabilities: false); [Theory, CombinatorialData] public Task OnAutoInsert_BraceFormattingWithTabs(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("\n", """ + => VerifyCSharpMarkupAndExpected("\n", """ class A { void M() {{|type:|} @@ -266,11 +267,11 @@ void M() $0 } } - """, mutatingLspWorkspace, insertSpaces: false, tabSize: 4, serverKind: WellKnownLspServerKinds.RazorLspServer); + """, mutatingLspWorkspace, insertSpaces: false, tabSize: 4, useVSCapabilities: false); [Theory, CombinatorialData] public Task OnAutoInsert_BraceFormattingInsideMethod(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("\n", """ + => VerifyCSharpMarkupAndExpected("\n", """ class A { void M() @@ -290,7 +291,7 @@ void M() } } } - """, mutatingLspWorkspace, serverKind: WellKnownLspServerKinds.RazorLspServer); + """, mutatingLspWorkspace, useVSCapabilities: false); [Theory, CombinatorialData] public Task OnAutoInsert_BraceFormattingNoResultInInterpolation(bool mutatingLspWorkspace) @@ -302,7 +303,7 @@ void M() var s = $"Hello {{|type:|} } } - """, mutatingLspWorkspace); + """, mutatingLspWorkspace, useVSCapabilities: false); [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1260219")] public Task OnAutoInsert_BraceFormattingDoesNotInsertExtraEmptyLines(bool mutatingLspWorkspace) @@ -315,7 +316,7 @@ void M() {|type:|} } } - """, mutatingLspWorkspace); + """, mutatingLspWorkspace, useVSCapabilities: false); [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1260219")] public Task OnAutoInsert_BraceFormattingDoesNotMoveCaretOnEnterInsideBraces(bool mutatingLspWorkspace) @@ -328,26 +329,75 @@ void M() } } - """, mutatingLspWorkspace); + """, mutatingLspWorkspace, useVSCapabilities: false); + + [Theory, CombinatorialData] + public Task OnAutoInsert_BraceFormattingOnNewLine(bool mutatingLspWorkspace) + => VerifyCSharpMarkupAndExpected("\n", """ + class A + { + void M() {{|type:|} + + } + } + """, """ + class A + { + void M() + { + $0 + } + } + """, mutatingLspWorkspace, useVSCapabilities: false); + + [Theory, CombinatorialData] + public Task OnAutoInsert_NoBraceFormattingForVS(bool mutatingLspWorkspace) + => VerifyNoResult("\n", """ + class A + { + void M() {{|type:|} + } + } + """, mutatingLspWorkspace, useVSCapabilities: true); - private async Task VerifyMarkupAndExpected( + [Theory, CombinatorialData] + public Task OnAutoInsert_BraceFormattingForRazorVS(bool mutatingLspWorkspace, bool useVSCapabilities) + => VerifyCSharpMarkupAndExpected("\n", """ + class A + { + void M() {{|type:|} + } + } + """, """ + class A + { + void M() + { + $0 + } + } + """, mutatingLspWorkspace, serverKind: WellKnownLspServerKinds.RazorLspServer, useVSCapabilities: useVSCapabilities); + + private async Task VerifyCSharpMarkupAndExpected( string characterTyped, - string markup, - string expected, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string expected, bool mutatingLspWorkspace, bool insertSpaces = true, int tabSize = 4, string languageName = LanguageNames.CSharp, - WellKnownLspServerKinds serverKind = WellKnownLspServerKinds.AlwaysActiveVSLspServer) + WellKnownLspServerKinds serverKind = WellKnownLspServerKinds.AlwaysActiveVSLspServer, + bool useVSCapabilities = true) { + var capbilities = GetCapabilities(useVSCapabilities); Task testLspServerTask; if (languageName == LanguageNames.CSharp) { - testLspServerTask = CreateTestLspServerAsync(markup, mutatingLspWorkspace, new InitializationOptions { ClientCapabilities = CapabilitiesWithVSExtensions, ServerKind = serverKind }); + testLspServerTask = CreateTestLspServerAsync(markup, mutatingLspWorkspace, new InitializationOptions { ClientCapabilities = capbilities, ServerKind = serverKind }); } else if (languageName == LanguageNames.VisualBasic) { - testLspServerTask = CreateVisualBasicTestLspServerAsync(markup, mutatingLspWorkspace, new InitializationOptions { ClientCapabilities = CapabilitiesWithVSExtensions, ServerKind = serverKind }); + testLspServerTask = CreateVisualBasicTestLspServerAsync(markup, mutatingLspWorkspace, new InitializationOptions { ClientCapabilities = capbilities, ServerKind = serverKind }); } else { @@ -368,9 +418,21 @@ private async Task VerifyMarkupAndExpected( Assert.Equal(expected, actualText); } - private async Task VerifyNoResult(string characterTyped, string markup, bool mutatingLspWorkspace, bool insertSpaces = true, int tabSize = 4) + private async Task VerifyNoResult( + string characterTyped, + string markup, + bool mutatingLspWorkspace, + bool insertSpaces = true, + int tabSize = 4, + WellKnownLspServerKinds serverKind = WellKnownLspServerKinds.AlwaysActiveVSLspServer, + bool useVSCapabilities = true) { - await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + var initilizationOptions = new InitializationOptions + { + ClientCapabilities = GetCapabilities(useVSCapabilities), + ServerKind = serverKind + }; + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, initilizationOptions); var locationTyped = testLspServer.GetLocations("type").Single(); var documentText = await (await testLspServer.GetDocumentAsync(locationTyped.DocumentUri)).GetTextAsync(); @@ -395,7 +457,7 @@ private static LSP.VSInternalDocumentOnAutoInsertParams CreateDocumentOnAutoInse LSP.Location locationTyped, bool insertSpaces, int tabSize) - => new LSP.VSInternalDocumentOnAutoInsertParams + => new() { Position = locationTyped.Range.Start, Character = characterTyped, diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs index c0177488c42..bc9c5b4e245 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs @@ -110,7 +110,7 @@ public async Task SwitchingContextsChangesDefaultContext(bool mutatingLspWorkspa } private static LSP.VSGetProjectContextsParams CreateGetProjectContextParams(DocumentUri uri) - => new LSP.VSGetProjectContextsParams() + => new() { TextDocument = new LSP.TextDocumentItem { DocumentUri = uri } }; diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs index b36ed1edb78..8f8624f2559 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs @@ -386,7 +386,7 @@ public void M() } private static LSP.ReferenceParams CreateReferenceParams(LSP.Location caret, IProgress progress) - => new LSP.ReferenceParams() + => new() { TextDocument = CreateTextDocumentIdentifier(caret.DocumentUri), Position = caret.Range.Start, diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Rename/PrepareRenameTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Rename/PrepareRenameTests.cs index 6ef1e4b9acf..a449ae19291 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Rename/PrepareRenameTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Rename/PrepareRenameTests.cs @@ -93,7 +93,7 @@ void M() } private static LSP.PrepareRenameParams CreatePrepareRenameParams(LSP.Location location) - => new LSP.PrepareRenameParams() + => new() { Position = location.Range.Start, TextDocument = CreateTextDocumentIdentifier(location.DocumentUri) diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs index ba43f0733ff..0c928fe2ed9 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs @@ -372,7 +372,7 @@ public void M() } private static LSP.RenameParams CreateRenameParams(LSP.Location location, string newName) - => new LSP.RenameParams() + => new() { NewName = newName, Position = location.Range.Start, diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs index 2392c4ad043..44ca13aa3f6 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs @@ -51,20 +51,20 @@ private protected static IReadOnlyDictionary GetTokenTypeToIndex(Te } private static LSP.SemanticTokensFullParams CreateSemanticTokensFullParams(LSP.Location caret) - => new LSP.SemanticTokensFullParams + => new() { TextDocument = new LSP.TextDocumentIdentifier { DocumentUri = caret.DocumentUri } }; private static LSP.SemanticTokensRangeParams CreateSemanticTokensRangeParams(LSP.Location location) - => new LSP.SemanticTokensRangeParams + => new() { TextDocument = new LSP.TextDocumentIdentifier { DocumentUri = location.DocumentUri }, Range = location.Range }; private static SemanticTokensRangesParams CreateSemanticTokensRangesParams(LSP.Location caret, Range[] ranges) - => new SemanticTokensRangesParams + => new() { TextDocument = new LSP.TextDocumentIdentifier { DocumentUri = caret.DocumentUri }, Ranges = ranges diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs index 5073b90d423..2365e65e5e6 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs @@ -111,7 +111,7 @@ public async Task TestGetSignatureHelpServerCapabilitiesAsync(bool mutatingLspWo } private static LSP.SignatureInformation CreateSignatureInformation(string methodLabal, string methodDocumentation, string parameterLabel, string parameterDocumentation) - => new LSP.SignatureInformation() + => new() { Documentation = CreateMarkupContent(LSP.MarkupKind.PlainText, methodDocumentation), Label = methodLabal, @@ -122,7 +122,7 @@ private static LSP.SignatureInformation CreateSignatureInformation(string method }; private static LSP.ParameterInformation CreateParameterInformation(string parameter, string documentation) - => new LSP.ParameterInformation() + => new() { Documentation = CreateMarkupContent(LSP.MarkupKind.PlainText, documentation), Label = parameter diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/SimplifyMethod/SimplifyMethodTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/SimplifyMethod/SimplifyMethodTests.cs index 5dcebdca295..a73ed3f28de 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/SimplifyMethod/SimplifyMethodTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/SimplifyMethod/SimplifyMethodTests.cs @@ -53,7 +53,7 @@ class A } private static SimplifyMethodParams CreateSimplifyMethodParams(LSP.Location location, string newText) - => new SimplifyMethodParams() + => new() { TextDocument = CreateTextDocumentIdentifier(location.DocumentUri), TextEdit = new TextEdit() { Range = location.Range, NewText = newText }, diff --git a/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/build/Microsoft.Net.Compilers.Toolset.props b/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/build/Microsoft.Net.Compilers.Toolset.props index 710e106bed4..4df1e637691 100644 --- a/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/build/Microsoft.Net.Compilers.Toolset.props +++ b/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/build/Microsoft.Net.Compilers.Toolset.props @@ -6,6 +6,9 @@ <_RoslynTargetDirectoryName Condition="'$(MSBuildRuntimeType)' != 'Core'">net472 <_RoslynTasksDirectory>$(MSBuildThisFileDirectory)..\tasks\$(_RoslynTargetDirectoryName)\ Custom + $(_RoslynTasksDirectory)bincore\ + $(RoslynCoreAssembliesPath) + $(_RoslynTasksDirectory) $(_RoslynTasksDirectory)Microsoft.Build.Tasks.CodeAnalysis.dll true $(_RoslynTasksDirectory)Microsoft.CSharp.Core.targets diff --git a/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/Framework/build/Microsoft.Net.Compilers.Toolset.Framework.Core.props b/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/Framework/build/Microsoft.Net.Compilers.Toolset.Framework.Core.props index 192ae2959ec..f94f7243e67 100644 --- a/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/Framework/build/Microsoft.Net.Compilers.Toolset.Framework.Core.props +++ b/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/Framework/build/Microsoft.Net.Compilers.Toolset.Framework.Core.props @@ -5,6 +5,7 @@ <_RoslynTargetDirectoryName>net472 <_RoslynTasksDirectory>$(MSBuildThisFileDirectory)..\tasks\$(_RoslynTargetDirectoryName)\ Custom + $(_RoslynTasksDirectory) $(_RoslynTasksDirectory)Microsoft.Build.Tasks.CodeAnalysis.dll true $(_RoslynTasksDirectory)Microsoft.CSharp.Core.targets diff --git a/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/arm64/build/Microsoft.Net.Compilers.Toolset.Arm64.props b/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/arm64/build/Microsoft.Net.Compilers.Toolset.Arm64.props index 499e9baaebb..5b5eeeaec82 100644 --- a/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/arm64/build/Microsoft.Net.Compilers.Toolset.Arm64.props +++ b/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/arm64/build/Microsoft.Net.Compilers.Toolset.Arm64.props @@ -4,6 +4,7 @@ <_RoslynTasksDirectory>$(MSBuildThisFileDirectory)..\tasks\net472\ Custom + $(_RoslynTasksDirectory) $(_RoslynTasksDirectory)Microsoft.Build.Tasks.CodeAnalysis.dll true $(_RoslynTasksDirectory)Microsoft.CSharp.Core.targets diff --git a/src/roslyn/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.Metrics/Microsoft.CodeAnalysis.Metrics.Package.csproj b/src/roslyn/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.Metrics/Microsoft.CodeAnalysis.Metrics.Package.csproj index 842a3f09379..b7a16d217ad 100644 --- a/src/roslyn/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.Metrics/Microsoft.CodeAnalysis.Metrics.Package.csproj +++ b/src/roslyn/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.Metrics/Microsoft.CodeAnalysis.Metrics.Package.csproj @@ -41,6 +41,8 @@ + + diff --git a/src/roslyn/src/RoslynAnalyzers/Tools/Metrics.Legacy/Metrics.Legacy.csproj b/src/roslyn/src/RoslynAnalyzers/Tools/Metrics.Legacy/Metrics.Legacy.csproj index f085a7df5c6..821bb31193c 100644 --- a/src/roslyn/src/RoslynAnalyzers/Tools/Metrics.Legacy/Metrics.Legacy.csproj +++ b/src/roslyn/src/RoslynAnalyzers/Tools/Metrics.Legacy/Metrics.Legacy.csproj @@ -27,6 +27,8 @@ + + diff --git a/src/roslyn/src/RoslynAnalyzers/Tools/Metrics/Metrics.csproj b/src/roslyn/src/RoslynAnalyzers/Tools/Metrics/Metrics.csproj index ff7a519a245..87a6765126f 100644 --- a/src/roslyn/src/RoslynAnalyzers/Tools/Metrics/Metrics.csproj +++ b/src/roslyn/src/RoslynAnalyzers/Tools/Metrics/Metrics.csproj @@ -25,6 +25,8 @@ + + diff --git a/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysisHelper.cs b/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysisHelper.cs index f3511e2f954..a39c4bdaeae 100644 --- a/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysisHelper.cs +++ b/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysisHelper.cs @@ -32,8 +32,7 @@ internal sealed class DisposeAnalysisHelper "System.IO.TextWriter", "System.Resources.IResourceReader", ]; - private static readonly BoundedCacheWithFactory s_DisposeHelperCache = - new(); + private static readonly BoundedCacheWithFactory s_DisposeHelperCache = new(); private static readonly ImmutableHashSet s_DisposableCreationKinds = ImmutableHashSet.Create( OperationKind.ObjectCreation, diff --git a/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysis.cs b/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysis.cs index 7dd00108a17..ea6dd28651c 100644 --- a/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysis.cs +++ b/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysis.cs @@ -24,8 +24,7 @@ public abstract class DataFlowAnalysis where TBlockAnalysisResult : AbstractBlockAnalysisResult { - private static readonly BoundedCache> s_resultCache = - new(); + private static readonly BoundedCache> s_resultCache = new(); protected DataFlowAnalysis(AbstractAnalysisDomain analysisDomain, DataFlowOperationVisitor operationVisitor) { diff --git a/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/LValueFlowCapturesProvider.cs b/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/LValueFlowCapturesProvider.cs index 1f675630f5f..fe689c29120 100644 --- a/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/LValueFlowCapturesProvider.cs +++ b/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/LValueFlowCapturesProvider.cs @@ -20,8 +20,7 @@ namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow /// internal static class LValueFlowCapturesProvider { - private static readonly ConditionalWeakTable> s_lValueFlowCapturesCache = - new(); + private static readonly ConditionalWeakTable> s_lValueFlowCapturesCache = new(); public static ImmutableHashSet GetOrCreateLValueFlowCaptures(ControlFlowGraph cfg) => s_lValueFlowCapturesCache.GetValue(cfg, CreateLValueFlowCaptures); diff --git a/src/roslyn/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs b/src/roslyn/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs index 6cf1af2ac8e..718747a8e8b 100644 --- a/src/roslyn/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs +++ b/src/roslyn/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs @@ -180,7 +180,10 @@ private bool CheckPackages(TextWriter textWriter) excludeFunc: relativeFileName => relativeFileName.StartsWith(@"tasks\netcore\bincore\Microsoft.DiaSymReader.Native", PathComparison) || relativeFileName.StartsWith(@"tasks\netcore\bincore\Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.dll", PathComparison) || - (relativeFileName.StartsWith(@"tasks\netcore\binfx\", PathComparison) && relativeFileName.EndsWith(".targets", PathComparison)), + (relativeFileName.StartsWith(@"tasks\netcore\binfx\", PathComparison) && relativeFileName.EndsWith(".targets", PathComparison)) || + relativeFileName.Equals(@"tasks\netcore\bincore\csc.exe", PathComparison) || + relativeFileName.Equals(@"tasks\netcore\bincore\vbc.exe", PathComparison) || + relativeFileName.Equals(@"tasks\netcore\bincore\VBCSCompiler.exe", PathComparison), (@"tasks\net472", GetProjectOutputDirectory("csc", "net472")), (@"tasks\net472", GetProjectOutputDirectory("vbc", "net472")), (@"tasks\net472", GetProjectOutputDirectory("csi", "net472")), diff --git a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/AbstractRazorCohostLifecycleService.cs b/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/AbstractRazorCohostLifecycleService.cs deleted file mode 100644 index 7e1720526fa..00000000000 --- a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/AbstractRazorCohostLifecycleService.cs +++ /dev/null @@ -1,17 +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.Threading; -using System.Threading.Tasks; -using Roslyn.LanguageServer.Protocol; - -namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost; - -internal abstract class AbstractRazorCohostLifecycleService : IDisposable -{ - public abstract Task LspServerIntializedAsync(CancellationToken cancellationToken); - public abstract Task RazorActivatedAsync(ClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext, CancellationToken cancellationToken); - public abstract void Dispose(); -} diff --git a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/Constants.cs b/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/Constants.cs index b909cb0ea84..dd849edb980 100644 --- a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/Constants.cs +++ b/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/Constants.cs @@ -13,6 +13,4 @@ internal static class Constants // These UI contexts are provided by Razor, so must match https://github.com/dotnet/razor/blob/main/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/RazorConstants.cs public static readonly Guid RazorCohostingUIContext = new Guid("6d5b86dc-6b8a-483b-ae30-098a3c7d6774"); - - public static readonly Guid RazorCapabilityPresentUIContext = new Guid("2077a158-ee71-484c-be76-350a1d49eaea"); } diff --git a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/Handlers/OnAutoInsert.cs b/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/Handlers/OnAutoInsert.cs index e688a3759f3..645103db784 100644 --- a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/Handlers/OnAutoInsert.cs +++ b/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/Handlers/OnAutoInsert.cs @@ -26,6 +26,6 @@ internal static class OnAutoInsert predicate: s => s.Metadata.Language == LanguageNames.CSharp, selector: s => s.Value); - return OnAutoInsertHandler.GetOnAutoInsertResponseAsync(globalOptions, services, document, linePosition, character, formattingOptions, isRazorRequest: true, cancellationToken); + return OnAutoInsertHandler.GetOnAutoInsertResponseAsync(globalOptions, services, document, linePosition, character, formattingOptions, includeNewLineBraceFormatting: true, cancellationToken); } } diff --git a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/ICohostStartupService.cs b/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/ICohostStartupService.cs index 87b350649cd..b35f55862b0 100644 --- a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/ICohostStartupService.cs +++ b/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/ICohostStartupService.cs @@ -8,7 +8,6 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost; -[Obsolete("Please move to AbstractRazorCohostLifecycleService. This will be removed in a future release.")] internal interface ICohostStartupService { Task StartupAsync(string serializedClientCapabilities, RazorCohostRequestContext requestContext, CancellationToken cancellationToken); diff --git a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/RazorStartupServiceFactory.cs b/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/RazorStartupServiceFactory.cs index 4d58661ee7a..05d547e6505 100644 --- a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/RazorStartupServiceFactory.cs +++ b/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/RazorStartupServiceFactory.cs @@ -22,34 +22,22 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost; [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed class RazorStartupServiceFactory( [Import(AllowDefault = true)] IUIContextActivationService? uIContextActivationService, - [Import(AllowDefault = true)] Lazy? cohostStartupService, - [Import(AllowDefault = true)] Lazy? razorCohostLifecycleService) : ILspServiceFactory + [Import(AllowDefault = true)] Lazy? cohostStartupService) : ILspServiceFactory { public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) { - return new RazorStartupService(uIContextActivationService, cohostStartupService, razorCohostLifecycleService); + return new RazorStartupService(uIContextActivationService, cohostStartupService); } private class RazorStartupService( IUIContextActivationService? uIContextActivationService, -#pragma warning disable CS0618 // Type or member is obsolete - Lazy? cohostStartupService, -#pragma warning restore CS0618 // Type or member is obsolete - Lazy? razorCohostLifecycleService) : ILspService, IOnInitialized, IDisposable + Lazy? cohostStartupService) : ILspService, IOnInitialized, IDisposable { private readonly CancellationTokenSource _disposalTokenSource = new(); private IDisposable? _cohostActivation; - private IDisposable? _razorFilePresentActivation; public void Dispose() { - if (razorCohostLifecycleService is { IsValueCreated: true, Value: var service }) - { - service.Dispose(); - } - - _razorFilePresentActivation?.Dispose(); - _razorFilePresentActivation = null; _cohostActivation?.Dispose(); _cohostActivation = null; _disposalTokenSource.Cancel(); @@ -63,53 +51,32 @@ public Task OnInitializedAsync(ClientCapabilities clientCapabilities, RequestCon return Task.CompletedTask; } - if (cohostStartupService is null && razorCohostLifecycleService is null) + if (cohostStartupService is null) { return Task.CompletedTask; } if (uIContextActivationService is null) { - PreinitializeRazor(); InitializeRazor(); } else { - // There are two initialization methods for Razor, which looks odd here, but are really controlled by UI contexts. - // This method fires for any Roslyn project, but not all Roslyn projects are Razor projects, so the first UI context - // triggers where there is a project with a Razor capability present in the solution, and the next is when a Razor file - // is opened in the editor. ie these two lines look the same, but really they do different levels of initialization. - _razorFilePresentActivation = uIContextActivationService.ExecuteWhenActivated(Constants.RazorCapabilityPresentUIContext, PreinitializeRazor); _cohostActivation = uIContextActivationService.ExecuteWhenActivated(Constants.RazorCohostingUIContext, InitializeRazor); } return Task.CompletedTask; - void PreinitializeRazor() - { - this.PreinitializeRazorAsync(_disposalTokenSource.Token).ReportNonFatalErrorAsync(); - } - void InitializeRazor() { this.InitializeRazorAsync(clientCapabilities, context, _disposalTokenSource.Token).ReportNonFatalErrorAsync(); } } - private async Task PreinitializeRazorAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) return; - - await TaskScheduler.Default.SwitchTo(alwaysYield: true); - - if (razorCohostLifecycleService is not null) - { - await razorCohostLifecycleService.Value.LspServerIntializedAsync(cancellationToken).ConfigureAwait(false); - } - } - private async Task InitializeRazorAsync(ClientCapabilities clientCapabilities, RequestContext context, CancellationToken cancellationToken) { + Contract.ThrowIfNull(cohostStartupService); + // The LSP server will dispose us when the server exits, but VS could decide to activate us later. // If a new instance of the server is created, a new instance of this class will be created and the // UIContext will already be active, so this method will be immediately called on the new instance. @@ -121,18 +88,9 @@ private async Task InitializeRazorAsync(ClientCapabilities clientCapabilities, R var requestContext = new RazorCohostRequestContext(context); - if (razorCohostLifecycleService is not null) - { - // If we have a cohost lifecycle service, fire post-initialization, which happens when the UIContext is activated. - await razorCohostLifecycleService.Value.RazorActivatedAsync(clientCapabilities, requestContext, cancellationToken).ConfigureAwait(false); - } - - if (cohostStartupService is not null) - { - // We use a string to pass capabilities to/from Razor to avoid version issues with the Protocol DLL - var serializedClientCapabilities = JsonSerializer.Serialize(clientCapabilities, ProtocolConversions.LspJsonSerializerOptions); - await cohostStartupService.Value.StartupAsync(serializedClientCapabilities, requestContext, cancellationToken).ConfigureAwait(false); - } + // We use a string to pass capabilities to/from Razor to avoid version issues with the Protocol DLL + var serializedClientCapabilities = JsonSerializer.Serialize(clientCapabilities, ProtocolConversions.LspJsonSerializerOptions); + await cohostStartupService.Value.StartupAsync(serializedClientCapabilities, requestContext, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/roslyn/src/Tools/PrepareTests/MinimizeUtil.cs b/src/roslyn/src/Tools/PrepareTests/MinimizeUtil.cs index 1731fc22059..0d767957de9 100644 --- a/src/roslyn/src/Tools/PrepareTests/MinimizeUtil.cs +++ b/src/roslyn/src/Tools/PrepareTests/MinimizeUtil.cs @@ -16,6 +16,11 @@ internal static class MinimizeUtil { internal record FilePathInfo(string RelativeDirectory, string Directory, string RelativePath, string FullPath); + /// + /// Special group for collecting files which need chmod +x. + /// + private static readonly Guid s_executablesGroup = new Guid("2665eb42-0a7d-4ea2-bb92-e4251d48df44"); + internal static void Run(string sourceDirectory, string destinationDirectory, bool isUnix) { const string duplicateDirectoryName = ".duplicate"; @@ -35,13 +40,13 @@ internal static void Run(string sourceDirectory, string destinationDirectory, bo CreateHardLink(outputPath, Path.Combine(sourceDirectory, individualFile)); } - // Map of all PE files MVID to the path information + // Map of all PE files MVID (or s_executablesGroup) to the path information var idToFilePathMap = initialWalk(); resolveDuplicates(); writeHydrateFile(); // The goal of initial walk is to - // 1. Record any PE files as they are eligable for de-dup + // 1. Record any PE files as they are eligible for de-dup // 2. Hard link all other files into destination directory Dictionary> initialWalk() { @@ -66,7 +71,7 @@ .. Directory.EnumerateDirectories(artifactsDir, "RunTests") return idToFilePathMap; } - static IEnumerable<(Guid mvid, FilePathInfo pathInfo)> walkDirectory(string unitDirPath, string sourceDirectory, string destinationDirectory) + IEnumerable<(Guid mvid, FilePathInfo pathInfo)> walkDirectory(string unitDirPath, string sourceDirectory, string destinationDirectory) { Console.WriteLine($"Walking {unitDirPath}"); string? lastOutputDirectory = null; @@ -95,6 +100,15 @@ .. Directory.EnumerateDirectories(artifactsDir, "RunTests") { var destFilePath = Path.Combine(currentOutputDirectory, fileName); CreateHardLink(destFilePath, sourceFilePath); + + if (isUnix && !OperatingSystem.IsWindows() && File.GetUnixFileMode(sourceFilePath).HasFlag(UnixFileMode.UserExecute)) + { + yield return (s_executablesGroup, new FilePathInfo( + RelativeDirectory: currentRelativeDirectory, + Directory: currentDirName, + RelativePath: Path.Combine(currentRelativeDirectory, fileName), + FullPath: sourceFilePath)); + } } } } @@ -104,6 +118,11 @@ void resolveDuplicates() { foreach (var pair in idToFilePathMap) { + if (pair.Key == s_executablesGroup) + { + continue; + } + if (pair.Value.Count > 1) { CreateHardLink(getPeFilePath(pair.Key), pair.Value[0].FullPath); @@ -185,6 +204,11 @@ static void writeWindowsRehydrateContent(StringBuilder builder, IGrouping RunAsync(ReplayOptions options) Console.WriteLine($"Pipe Name: {options.PipeName}"); Console.WriteLine($"Parallel: {options.MaxParallel}"); Console.WriteLine($"Iterations: {options.Iterations}"); + + var sumBuildTime = TimeSpan.Zero; + var sumTotalTime = TimeSpan.Zero; + try + { + var compilerCalls = ReadAllCompilerCalls(options.BinlogPath); + Console.WriteLine($"Compilation Events: {compilerCalls.Count}"); + + for (var i = 0; i < options.Iterations; i++) + { + Console.WriteLine(); + Console.WriteLine($"Iteration: {i + 1}"); + var (buildTime, totalTime) = await RunOneAsync(compilerCalls, options).ConfigureAwait(false); + Console.WriteLine($"Build Time: {buildTime:mm\\:ss}"); + Console.WriteLine($"Total Time: {totalTime:mm\\:ss}"); + + sumBuildTime += buildTime; + sumTotalTime += totalTime; + } + + if (options.Iterations > 1) + { + Console.WriteLine(); + Console.WriteLine($"Compilation Events: {compilerCalls.Count}"); + Console.WriteLine($"Average Build Time: {TimeSpan.FromTicks(sumBuildTime.Ticks / options.Iterations):mm\\:ss}"); + Console.WriteLine($"Average Total Time: {TimeSpan.FromTicks(sumTotalTime.Ticks / options.Iterations):mm\\:ss}"); + } + + foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables()) + { + if (entry.Key is string key && key.StartsWith("DOTNET_", StringComparison.OrdinalIgnoreCase)) + { + Console.WriteLine($"{key}={entry.Value}"); + } + } + + return 0; + } + catch (Exception ex) + { + Console.WriteLine($"Error: {ex.Message}"); + return 1; + } +} + +static async Task<(TimeSpan BuildTime, TimeSpan TotalTime)> RunOneAsync(List compilerCalls, ReplayOptions options) +{ Console.WriteLine(); Console.WriteLine("Starting server"); + if (Directory.Exists(options.OutputDirectory)) + { + Directory.Delete(options.OutputDirectory, recursive: true); + } + Directory.CreateDirectory(options.OutputDirectory); + Directory.CreateDirectory(options.TempDirectory); + using var compilerServerLogger = new CompilerServerLogger("replay", Path.Combine(options.OutputDirectory, "server.log")); if (!BuildServerConnection.TryCreateServer(options.ClientDirectory, options.PipeName, compilerServerLogger, out int serverProcessId)) { - Console.WriteLine("Failed to start server"); - return 1; + throw new Exception("Failed to create server"); } - Console.WriteLine($"Process Id: {serverProcessId}"); + Console.WriteLine($"VBCSCompiler Process Id: {serverProcessId}"); if (options.Wait) { Console.WriteLine("Press any key to continue"); @@ -95,28 +143,17 @@ static async Task RunAsync(ReplayOptions options) Console.WriteLine("Continuing"); } + var stopwatch = new Stopwatch(); + stopwatch.Start(); + TimeSpan buildTime; try { - for (var i = 0; i < options.Iterations; i++) + await foreach (var buildData in BuildAllAsync(options, compilerCalls, compilerServerLogger, CancellationToken.None).ConfigureAwait(false)) { - Console.WriteLine(); - Console.WriteLine($"Iteration: {i + 1}"); - var compilerCalls = ReadAllCompilerCalls(options.BinlogPath); - var stopwatch = new Stopwatch(); - stopwatch.Start(); - - await foreach (var buildData in BuildAllAsync(options, compilerCalls, compilerServerLogger, CancellationToken.None).ConfigureAwait(false)) - { - Console.WriteLine($"{buildData.CompilerCall.GetDiagnosticName()} ... {buildData.BuildResponse.Type}"); - } - - stopwatch.Stop(); - Console.WriteLine($"Pipe Name: {options.PipeName}"); - Console.WriteLine($"Compilation Events: {compilerCalls.Count}"); - Console.WriteLine($"Time: {stopwatch.Elapsed:mm\\:ss}"); + Console.WriteLine($"{buildData.CompilerCall.GetDiagnosticName()} ... {buildData.BuildResponse.Type}"); } - return 0; + buildTime = stopwatch.Elapsed; } finally { @@ -129,6 +166,8 @@ await BuildServerConnection.RunServerShutdownRequestAsync( compilerServerLogger, cancellationToken: CancellationToken.None).ConfigureAwait(false); } + + return (buildTime, stopwatch.Elapsed); } static List ReadAllCompilerCalls(string binlogPath) diff --git a/src/roslyn/src/Tools/Replay/Replay.csproj b/src/roslyn/src/Tools/Replay/Replay.csproj index 180c31962b9..219d34dfc76 100644 --- a/src/roslyn/src/Tools/Replay/Replay.csproj +++ b/src/roslyn/src/Tools/Replay/Replay.csproj @@ -1,7 +1,7 @@  Exe - $(NetRoslyn);net472 + $(NetRoslynSourceBuild);net472 true false diff --git a/src/roslyn/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt b/src/roslyn/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt index 6b1f60e975c..f21bf53ee7d 100644 --- a/src/roslyn/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt +++ b/src/roslyn/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt @@ -686,6 +686,7 @@ Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions.#ctor(Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions,System.Action{System.Exception,Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer,Microsoft.CodeAnalysis.Diagnostic},System.Boolean,System.Boolean) Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions.#ctor(Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions,System.Action{System.Exception,Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer,Microsoft.CodeAnalysis.Diagnostic},System.Boolean,System.Boolean,System.Boolean) Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions.#ctor(Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions,System.Action{System.Exception,Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer,Microsoft.CodeAnalysis.Diagnostic},System.Boolean,System.Boolean,System.Boolean,System.Func{System.Exception,System.Boolean}) +Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions.#ctor(Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions,System.Action{System.Exception,Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer,Microsoft.CodeAnalysis.Diagnostic},System.Boolean,System.Boolean,System.Boolean,System.Func{System.Exception,System.Boolean},System.Func{Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer,Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider}) Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions.get_AnalyzerExceptionFilter Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions.get_ConcurrentAnalysis Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions.get_LogAnalyzerExecutionTime @@ -1230,6 +1231,7 @@ Microsoft.CodeAnalysis.IMethodSymbol.GetTypeInferredDuringReduction(Microsoft.Co Microsoft.CodeAnalysis.IMethodSymbol.ReduceExtensionMethod(Microsoft.CodeAnalysis.ITypeSymbol) Microsoft.CodeAnalysis.IMethodSymbol.get_Arity Microsoft.CodeAnalysis.IMethodSymbol.get_AssociatedAnonymousDelegate +Microsoft.CodeAnalysis.IMethodSymbol.get_AssociatedExtensionImplementation Microsoft.CodeAnalysis.IMethodSymbol.get_AssociatedSymbol Microsoft.CodeAnalysis.IMethodSymbol.get_CallingConvention Microsoft.CodeAnalysis.IMethodSymbol.get_ConstructedFrom @@ -1284,8 +1286,10 @@ Microsoft.CodeAnalysis.INamedTypeSymbol.get_ConstructedFrom Microsoft.CodeAnalysis.INamedTypeSymbol.get_Constructors Microsoft.CodeAnalysis.INamedTypeSymbol.get_DelegateInvokeMethod Microsoft.CodeAnalysis.INamedTypeSymbol.get_EnumUnderlyingType +Microsoft.CodeAnalysis.INamedTypeSymbol.get_ExtensionParameter Microsoft.CodeAnalysis.INamedTypeSymbol.get_InstanceConstructors Microsoft.CodeAnalysis.INamedTypeSymbol.get_IsComImport +Microsoft.CodeAnalysis.INamedTypeSymbol.get_IsExtension Microsoft.CodeAnalysis.INamedTypeSymbol.get_IsFileLocal Microsoft.CodeAnalysis.INamedTypeSymbol.get_IsGenericType Microsoft.CodeAnalysis.INamedTypeSymbol.get_IsImplicitClass diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs b/src/roslyn/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs index 44484a483a7..653985f86f5 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs @@ -2227,26 +2227,26 @@ internal StyleViewModel(OptionStore optionStore, IServiceProvider serviceProvide var usingDirectivePlacementPreferences = new List { - new CodeStylePreference(CSharpVSResources.Inside_namespace, isChecked: false), - new CodeStylePreference(CSharpEditorResources.Outside_namespace, isChecked: false), + new(CSharpVSResources.Inside_namespace, isChecked: false), + new(CSharpEditorResources.Outside_namespace, isChecked: false), }; var qualifyMemberAccessPreferences = new List { - new CodeStylePreference(CSharpVSResources.Prefer_this, isChecked: true), - new CodeStylePreference(CSharpVSResources.Do_not_prefer_this, isChecked: false), + new(CSharpVSResources.Prefer_this, isChecked: true), + new(CSharpVSResources.Do_not_prefer_this, isChecked: false), }; var predefinedTypesPreferences = new List { - new CodeStylePreference(ServicesVSResources.Prefer_predefined_type, isChecked: true), - new CodeStylePreference(ServicesVSResources.Prefer_framework_type, isChecked: false), + new(ServicesVSResources.Prefer_predefined_type, isChecked: true), + new(ServicesVSResources.Prefer_framework_type, isChecked: false), }; var typeStylePreferences = new List { - new CodeStylePreference(CSharpVSResources.Prefer_var, isChecked: true), - new CodeStylePreference(CSharpVSResources.Prefer_explicit_type, isChecked: false), + new(CSharpVSResources.Prefer_var, isChecked: true), + new(CSharpVSResources.Prefer_explicit_type, isChecked: false), }; CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CodeStyleOptions2.QualifyFieldAccess, CSharpVSResources.Qualify_field_access_with_this, s_fieldDeclarationPreviewTrue, s_fieldDeclarationPreviewFalse, this, optionStore, qualifyGroupTitle, qualifyMemberAccessPreferences)); @@ -2370,9 +2370,9 @@ private void AddBracesOptions(OptionStore optionStore, string bracesPreferenceGr { var bracesPreferences = new List { - new CodeStylePreference(EditorFeaturesResources.Yes, isChecked: false), - new CodeStylePreference(EditorFeaturesResources.No, isChecked: false), - new CodeStylePreference(CSharpVSResources.When_on_multiple_lines, isChecked: false), + new(EditorFeaturesResources.Yes, isChecked: false), + new(EditorFeaturesResources.No, isChecked: false), + new(CSharpVSResources.When_on_multiple_lines, isChecked: false), }; var enumValues = new[] { PreferBracesPreference.Always, PreferBracesPreference.None, PreferBracesPreference.WhenMultiline }; @@ -2389,8 +2389,8 @@ private void AddNamespaceDeclarationsOptions(OptionStore optionStore, string gro { var preferences = new List { - new CodeStylePreference(CSharpVSResources.Block_scoped, isChecked: false), - new CodeStylePreference(CSharpVSResources.File_scoped, isChecked: false), + new(CSharpVSResources.Block_scoped, isChecked: false), + new(CSharpVSResources.File_scoped, isChecked: false), }; var enumValues = new[] { NamespaceDeclarationPreference.BlockScoped, NamespaceDeclarationPreference.FileScoped }; @@ -2407,9 +2407,9 @@ private void AddExpressionBodyOptions(OptionStore optionStore, string expression { var expressionBodyPreferences = new List { - new CodeStylePreference(ServicesVSResources.Never, isChecked: false), - new CodeStylePreference(CSharpVSResources.When_possible, isChecked: false), - new CodeStylePreference(CSharpVSResources.When_on_single_line, isChecked: false), + new(ServicesVSResources.Never, isChecked: false), + new(CSharpVSResources.When_possible, isChecked: false), + new(CSharpVSResources.When_on_single_line, isChecked: false), }; var enumValues = new[] { ExpressionBodyPreference.Never, ExpressionBodyPreference.WhenPossible, ExpressionBodyPreference.WhenOnSingleLine }; @@ -2475,8 +2475,8 @@ private void AddUnusedValueOptions(OptionStore optionStore, string expressionPre { var unusedValuePreferences = new List { - new CodeStylePreference(ServicesVSResources.Unused_local, isChecked: false), - new CodeStylePreference(CSharpVSResources.Discard, isChecked: true), + new(ServicesVSResources.Unused_local, isChecked: false), + new(CSharpVSResources.Discard, isChecked: true), }; var enumValues = new[] diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/Snippets/CSharpSnippetExpansionLanguageHelper.cs b/src/roslyn/src/VisualStudio/CSharp/Impl/Snippets/CSharpSnippetExpansionLanguageHelper.cs index 2fee5a72500..ca069b3a27e 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/Snippets/CSharpSnippetExpansionLanguageHelper.cs +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/Snippets/CSharpSnippetExpansionLanguageHelper.cs @@ -88,7 +88,7 @@ public override async Task AddImportsAsync( // In Venus/Razor, inserting imports statements into the subject buffer does not work. // Instead, we add the imports through the contained language host. - if (TryAddImportsToContainedDocument(document, newUsingDirectives.Where(u => u.Alias == null).Select(u => u.Name!.ToString()))) + if (TryAddImportsToContainedDocument(document, newUsingDirectives.SelectAsArray(u => u.Alias == null, u => u.Name!.ToString()))) return document; var addImportService = document.GetRequiredLanguageService(); diff --git a/src/roslyn/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs b/src/roslyn/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs index 74e0f3482d5..b2da5fa1910 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs +++ b/src/roslyn/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs @@ -487,7 +487,7 @@ public async Task TestOpenWithSolutionKeyReadWithDocumentKey(Size size, [Combina } { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1)); Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1))); } @@ -508,7 +508,7 @@ public async Task TestOpenWithSolutionKeyReadWithDocument(Size size, [Combinator } { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1)); Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1))); } @@ -619,7 +619,7 @@ public async Task TestOpenWithSolutionKeyReadWithDocumentKeyAndDocument1(Size si } { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1)); Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1))); @@ -643,7 +643,7 @@ public async Task TestOpenWithSolutionKeyReadWithDocumentKeyAndDocument2(Size si } { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1)); Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1))); @@ -662,12 +662,12 @@ public async Task TestOpenWithSolutionKeyReadWithDocumentKey_WriteWithSolutionKe var streamName1 = "stream"; { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1); } { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1)); Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1))); } @@ -683,12 +683,12 @@ public async Task TestOpenWithSolutionKeyReadWithDocument_WriteWithSolutionKey(S var streamName1 = "stream"; { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1); } { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1)); Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1))); } @@ -704,7 +704,7 @@ public async Task TestOpenWithSolutionReadWithDocumentKey_WriteWithSolutionKey(S var streamName1 = "stream"; { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1); } @@ -725,7 +725,7 @@ public async Task TestOpenWithSolutionReadWithDocument_WriteWithSolutionKey(Size var streamName1 = "stream"; { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1); } @@ -746,7 +746,7 @@ public async Task TestOpenWithSolutionReadWithDocumentKeyAndDocument1_WriteWithS var streamName1 = "stream"; { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1); } @@ -770,7 +770,7 @@ public async Task TestOpenWithSolutionReadWithDocumentKeyAndDocument2_WriteWithS var streamName1 = "stream"; { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1); } @@ -794,12 +794,12 @@ public async Task TestOpenWithSolutionKeyReadWithDocumentKeyAndDocument1_WriteWi var streamName1 = "stream"; { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1); } { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1)); Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1))); @@ -818,12 +818,12 @@ public async Task TestOpenWithSolutionKeyReadWithDocumentKeyAndDocument2_WriteWi var streamName1 = "stream"; { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1); } { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1)); Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1))); @@ -1008,7 +1008,7 @@ internal async Task GetStorageAsync( _storageService?.GetTestAccessor().Shutdown(); var configuration = new MockPersistentStorageConfiguration(solution.Id, persistentFolder.Path, throwOnFailure); - _storageService = (AbstractPersistentStorageService)solution.Workspace.Services.SolutionServices.GetPersistentStorageService(); + _storageService = (AbstractPersistentStorageService)solution.Services.GetPersistentStorageService(); var storage = await _storageService.GetStorageAsync( SolutionKey.ToSolutionKey(solution), configuration, faultInjector, CancellationToken.None); @@ -1022,11 +1022,11 @@ internal async Task GetStorageAsync( } internal async Task GetStorageFromKeyAsync( - HostWorkspaceServices services, SolutionKey solutionKey, IPersistentStorageFaultInjector? faultInjector = null) + SolutionServices services, SolutionKey solutionKey, IPersistentStorageFaultInjector? faultInjector = null) { var configuration = new MockPersistentStorageConfiguration(solutionKey.Id, _persistentFolder.Path, throwOnFailure: true); - _storageService = (AbstractPersistentStorageService)services.SolutionServices.GetPersistentStorageService(); + _storageService = (AbstractPersistentStorageService)services.GetPersistentStorageService(); var storage = await _storageService.GetStorageAsync( solutionKey, configuration, faultInjector, CancellationToken.None); diff --git a/src/roslyn/src/VisualStudio/Core/Def/ChangeSignature/ChangeSignatureDialogViewModel.cs b/src/roslyn/src/VisualStudio/Core/Def/ChangeSignature/ChangeSignatureDialogViewModel.cs index fc34204534f..fb601810abe 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/ChangeSignature/ChangeSignatureDialogViewModel.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/ChangeSignature/ChangeSignatureDialogViewModel.cs @@ -279,8 +279,8 @@ internal ParameterConfiguration GetParameterConfiguration() { return new ParameterConfiguration( _originalParameterConfiguration.ThisParameter, - [.. _parametersWithoutDefaultValues.Where(p => !p.IsRemoved).Select(p => p.Parameter)], - [.. _parametersWithDefaultValues.Where(p => !p.IsRemoved).Select(p => p.Parameter)], + _parametersWithoutDefaultValues.SelectAsArray(p => !p.IsRemoved, p => p.Parameter), + _parametersWithDefaultValues.SelectAsArray(p => !p.IsRemoved, p => p.Parameter), (_paramsParameter == null || _paramsParameter.IsRemoved) ? null : (ExistingParameter)_paramsParameter.Parameter, selectedIndex: -1); } diff --git a/src/roslyn/src/VisualStudio/Core/Def/Diagnostics/IVisualStudioDiagnosticAnalyzerService.cs b/src/roslyn/src/VisualStudio/Core/Def/Diagnostics/IVisualStudioDiagnosticAnalyzerService.cs index 1545012d679..bf59c6afe2a 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/Diagnostics/IVisualStudioDiagnosticAnalyzerService.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/Diagnostics/IVisualStudioDiagnosticAnalyzerService.cs @@ -22,7 +22,7 @@ internal interface IVisualStudioDiagnosticAnalyzerService /// /// This is used by the Ruleset Editor from ManagedSourceCodeAnalysis.dll in VisualStudio. /// - IReadOnlyDictionary> GetAllDiagnosticDescriptors(IVsHierarchy? hierarchy); + Task>> GetAllDiagnosticDescriptorsAsync(IVsHierarchy? hierarchy, CancellationToken cancellationToken); /// /// Runs all the applicable NuGet and VSIX diagnostic analyzers for the given project OR current solution in background and updates the error list. diff --git a/src/roslyn/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerService.cs b/src/roslyn/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerService.cs index 1ff0c6a715d..08839c96cec 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerService.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerService.cs @@ -34,7 +34,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics; internal sealed partial class VisualStudioDiagnosticAnalyzerService( VisualStudioWorkspace workspace, IVsService statusbar, - DiagnosticAnalyzerInfoCache.SharedGlobalCache diagnosticAnalyzerInfoCache, IThreadingContext threadingContext, IVsHierarchyItemManager vsHierarchyItemManager, IAsynchronousOperationListenerProvider listenerProvider) : IVisualStudioDiagnosticAnalyzerService @@ -45,7 +44,6 @@ internal sealed partial class VisualStudioDiagnosticAnalyzerService( private readonly VisualStudioWorkspace _workspace = workspace; private readonly IVsService _statusbar = statusbar; - private readonly DiagnosticAnalyzerInfoCache _diagnosticAnalyzerInfoCache = diagnosticAnalyzerInfoCache.AnalyzerInfoCache; private readonly IThreadingContext _threadingContext = threadingContext; private readonly IVsHierarchyItemManager _vsHierarchyItemManager = vsHierarchyItemManager; private readonly IAsynchronousOperationListener _listener = listenerProvider.GetListener(FeatureAttribute.DiagnosticService); @@ -69,14 +67,19 @@ public async Task InitializeAsync(IAsyncServiceProvider serviceProvider, Cancell } } - public IReadOnlyDictionary> GetAllDiagnosticDescriptors(IVsHierarchy? hierarchy) + public async Task>> GetAllDiagnosticDescriptorsAsync( + IVsHierarchy? hierarchy, + CancellationToken cancellationToken) { var currentSolution = _workspace.CurrentSolution; var hostAnalyzers = currentSolution.SolutionState.Analyzers; + var diagnosticService = currentSolution.Services.GetRequiredService(); if (hierarchy == null) { - return Transform(hostAnalyzers.GetDiagnosticDescriptorsPerReference(_diagnosticAnalyzerInfoCache)); + return Transform( + await diagnosticService.GetDiagnosticDescriptorsPerReferenceAsync( + currentSolution, cancellationToken).ConfigureAwait(false)); } // Analyzers are only supported for C# and VB currently. @@ -88,8 +91,8 @@ public IReadOnlyDictionary> GetAllDiag { var project = projectsWithHierarchy.FirstOrDefault(); return project == null - ? Transform(hostAnalyzers.GetDiagnosticDescriptorsPerReference(_diagnosticAnalyzerInfoCache)) - : Transform(hostAnalyzers.GetDiagnosticDescriptorsPerReference(_diagnosticAnalyzerInfoCache, project)); + ? Transform(await diagnosticService.GetDiagnosticDescriptorsPerReferenceAsync(currentSolution, cancellationToken).ConfigureAwait(false)) + : Transform(await diagnosticService.GetDiagnosticDescriptorsPerReferenceAsync(project, cancellationToken).ConfigureAwait(false)); } else { @@ -98,7 +101,8 @@ public IReadOnlyDictionary> GetAllDiag var descriptorsMap = ImmutableDictionary.CreateBuilder>(); foreach (var project in projectsWithHierarchy) { - var descriptorsPerReference = hostAnalyzers.GetDiagnosticDescriptorsPerReference(_diagnosticAnalyzerInfoCache, project); + var descriptorsPerReference = await diagnosticService.GetDiagnosticDescriptorsPerReferenceAsync( + project, cancellationToken).ConfigureAwait(false); foreach (var (displayName, descriptors) in descriptorsPerReference) { if (descriptorsMap.TryGetValue(displayName, out var existingDescriptors)) diff --git a/src/roslyn/src/VisualStudio/Core/Def/DocumentOutline/DocumentSymbolDataViewModel.cs b/src/roslyn/src/VisualStudio/Core/Def/DocumentOutline/DocumentSymbolDataViewModel.cs index d6d5e67eaa4..d78de01d96f 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/DocumentOutline/DocumentSymbolDataViewModel.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/DocumentOutline/DocumentSymbolDataViewModel.cs @@ -48,8 +48,8 @@ public DocumentSymbolDataViewModel( Children = children; } - private static readonly PropertyChangedEventArgs _isExpandedPropertyChangedEventArgs = new PropertyChangedEventArgs(nameof(IsExpanded)); - private static readonly PropertyChangedEventArgs _isSelectedPropertyChangedEventArgs = new PropertyChangedEventArgs(nameof(IsSelected)); + private static readonly PropertyChangedEventArgs _isExpandedPropertyChangedEventArgs = new(nameof(IsExpanded)); + private static readonly PropertyChangedEventArgs _isSelectedPropertyChangedEventArgs = new(nameof(IsSelected)); private void NotifyPropertyChanged([CallerMemberName] string? propertyName = null) => PropertyChanged?.Invoke(this, propertyName switch diff --git a/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/LegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor.cs b/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/LegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor.cs index 821841df214..35797125575 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/LegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/LegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using System.Composition; +using System.Threading; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ExternalAccess.LegacyCodeAnalysis.Api; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics; @@ -15,11 +17,13 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.LegacyCodeAnalysis; [Export(typeof(ILegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor)), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class LegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor(IVisualStudioDiagnosticAnalyzerService implementation) +internal sealed class LegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor( + IThreadingContext threadingContext, + IVisualStudioDiagnosticAnalyzerService implementation) : ILegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor { public IReadOnlyDictionary> GetAllDiagnosticDescriptors(IVsHierarchy hierarchyOpt) - => implementation.GetAllDiagnosticDescriptors(hierarchyOpt); + => threadingContext.JoinableTaskFactory.Run(() => implementation.GetAllDiagnosticDescriptorsAsync(hierarchyOpt, CancellationToken.None)); public void RunAnalyzers(IVsHierarchy hierarchyOpt) => implementation.RunAnalyzers(hierarchyOpt); diff --git a/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/UnitTesting/VisualStudioGlobalOperationNotificationService.cs b/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/UnitTesting/VisualStudioGlobalOperationNotificationService.cs index b093f5376f2..0c6c2be2c7c 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/UnitTesting/VisualStudioGlobalOperationNotificationService.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/UnitTesting/VisualStudioGlobalOperationNotificationService.cs @@ -22,8 +22,8 @@ internal sealed partial class VisualStudioGlobalOperationNotificationService : A [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public VisualStudioGlobalOperationNotificationService( - IThreadingContext threadingContext, - IAsynchronousOperationListenerProvider listenerProvider) : base(listenerProvider, threadingContext.DisposalToken) + IThreadingContext threadingContext, + IAsynchronousOperationListenerProvider listenerProvider) : base(listenerProvider, threadingContext.DisposalToken) { _solutionEventMonitor = new SolutionEventMonitor(this); diff --git a/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/VSTypeScript/Api/VSTypeScriptVisualStudioProjectWrapper.LSPContainedDocumentServiceProvider.cs b/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/VSTypeScript/Api/VSTypeScriptVisualStudioProjectWrapper.LSPContainedDocumentServiceProvider.cs index 2d6909e1378..63213cc907c 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/VSTypeScript/Api/VSTypeScriptVisualStudioProjectWrapper.LSPContainedDocumentServiceProvider.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/VSTypeScript/Api/VSTypeScriptVisualStudioProjectWrapper.LSPContainedDocumentServiceProvider.cs @@ -17,7 +17,7 @@ private LspContainedDocumentServiceProvider() _documentPropertiesService = VirtualDocumentPropertiesService.Instance; } - public static LspContainedDocumentServiceProvider Instance = new LspContainedDocumentServiceProvider(); + public static LspContainedDocumentServiceProvider Instance = new(); bool IDocumentOperationService.CanApplyChange => true; @@ -39,7 +39,7 @@ private sealed class VirtualDocumentPropertiesService : DocumentPropertiesServic private VirtualDocumentPropertiesService() { } - public static VirtualDocumentPropertiesService Instance = new VirtualDocumentPropertiesService(); + public static VirtualDocumentPropertiesService Instance = new(); public override string? DiagnosticsLspClientName => _lspClientName; } diff --git a/src/roslyn/src/VisualStudio/Core/Def/ExtractInterface/VisualStudioExtractInterfaceOptionsService.cs b/src/roslyn/src/VisualStudio/Core/Def/ExtractInterface/VisualStudioExtractInterfaceOptionsService.cs index da8e523a027..99af5047e53 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/ExtractInterface/VisualStudioExtractInterfaceOptionsService.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/ExtractInterface/VisualStudioExtractInterfaceOptionsService.cs @@ -74,7 +74,7 @@ public ExtractInterfaceOptionsResult GetExtractInterfaceOptions( if (result.HasValue && result.Value) { - var includedMembers = viewModel.MemberContainers.Where(c => c.IsChecked).Select(c => c.Symbol); + var includedMembers = viewModel.MemberContainers.SelectAsArray(c => c.IsChecked, c => c.Symbol); return new ExtractInterfaceOptionsResult( isCancelled: false, diff --git a/src/roslyn/src/VisualStudio/Core/Def/GenerateType/GenerateTypeDialogViewModel.cs b/src/roslyn/src/VisualStudio/Core/Def/GenerateType/GenerateTypeDialogViewModel.cs index 2d9f8061648..46e7c2d09c8 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/GenerateType/GenerateTypeDialogViewModel.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/GenerateType/GenerateTypeDialogViewModel.cs @@ -689,7 +689,7 @@ internal GenerateTypeDialogViewModel( { // Populate the project list // Add the current project - new ProjectSelectItem(document.Project) + new(document.Project) }; // Add the rest of the projects diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsContainedLanguageFactory.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsContainedLanguageFactory.cs index eb57d578616..6da3f32f5af 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsContainedLanguageFactory.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsContainedLanguageFactory.cs @@ -61,7 +61,7 @@ private ProjectSystemProject FindMatchingProject(IVsHierarchy hierarchy, uint it return null; } - return this.Workspace.GetProjectWithHierarchyAndName(hierarchy, projectName); + return this.Workspace.Value.GetProjectWithHierarchyAndName(hierarchy, projectName); } public int GetLanguage(IVsHierarchy hierarchy, uint itemid, IVsTextBufferCoordinator bufferCoordinator, out IVsContainedLanguage language) diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsImmediateStatementCompletion2.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsImmediateStatementCompletion2.cs index 81f5773e507..90c4b7e7c7d 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsImmediateStatementCompletion2.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsImmediateStatementCompletion2.cs @@ -9,6 +9,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.LanguageServices.Implementation.DebuggerIntelliSense; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; @@ -50,8 +51,10 @@ int IVsImmediateStatementCompletion2.InstallStatementCompletion(int install, IVs { if (!this.filters.TryGetValue(textView, out var filter)) { + var editorAdaptersFactoryService = this.Package.ComponentModel.GetService(); + filter = new DebuggerIntelliSenseFilter( - this.EditorAdaptersFactoryService.GetWpfTextView(textView), + editorAdaptersFactoryService.GetWpfTextView(textView), this.Package.ComponentModel, this.Package.ComponentModel.GetService()); this.filters[textView] = filter; @@ -83,13 +86,14 @@ int IVsImmediateStatementCompletion2.SetCompletionContext(string filePath, // commit, so we need to drag the IVsTextLines around, too. Marshal.ThrowExceptionForHR(textView.GetBuffer(out var debuggerBuffer)); - var view = EditorAdaptersFactoryService.GetWpfTextView(textView); + var editorAdaptersFactoryService = this.Package.ComponentModel.GetService(); + var view = editorAdaptersFactoryService.GetWpfTextView(textView); // Sometimes, they give us a null context buffer. In that case, there's probably not any // work to do. if (buffer != null) { - var contextBuffer = EditorAdaptersFactoryService.GetDataBuffer(buffer); + var contextBuffer = editorAdaptersFactoryService.GetDataBuffer(buffer); if (!contextBuffer.ContentType.IsOfType(this.ContentTypeName)) { diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageBlock.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageBlock.cs index 3fea5f9bf7e..72056f1f482 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageBlock.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageBlock.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; +using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.LanguageServices.Implementation.Extensions; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Utilities; @@ -29,7 +30,9 @@ public int GetCurrentBlock( out string pbstrDescription, out int pfBlockAvailable) { - var snapshot = this.EditorAdaptersFactoryService.GetDataBuffer(pTextLines).CurrentSnapshot; + var editorAdaptersFactoryService = this.Package.ComponentModel.GetService(); + + var snapshot = editorAdaptersFactoryService.GetDataBuffer(pTextLines).CurrentSnapshot; var position = snapshot?.TryGetPosition(iCurrentLine, iCurrentChar); if (position == null) { diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageContextProvider.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageContextProvider.cs index e2da72836fe..490d0fd8b78 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageContextProvider.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageContextProvider.cs @@ -5,6 +5,7 @@ using System.Threading; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.LanguageServices.Implementation.F1Help; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.TextManager.Interop; @@ -17,7 +18,8 @@ public int UpdateLanguageContext(uint dwHint, IVsTextLines pBuffer, Microsoft.Vi { return this.ThreadingContext.JoinableTaskFactory.Run(async () => { - var textBuffer = EditorAdaptersFactoryService.GetDataBuffer(pBuffer); + var editorAdaptersFactoryService = this.Package.ComponentModel.GetService(); + var textBuffer = editorAdaptersFactoryService.GetDataBuffer(pBuffer); var context = (IVsUserContext)pUC; if (textBuffer == null || context == null) diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs index 8cd5493da86..ccfa3c6b42e 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs @@ -20,7 +20,7 @@ int IVsLanguageDebugInfo.GetLanguageID(IVsTextBuffer pBuffer, int iLine, int iCo { try { - return _languageDebugInfo.GetLanguageID(pBuffer, iLine, iCol, out pguidLanguageID); + return _languageDebugInfo.Value.GetLanguageID(pBuffer, iLine, iCol, out pguidLanguageID); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { @@ -32,7 +32,7 @@ int IVsLanguageDebugInfo.GetLocationOfName(string pszName, out string pbstrMkDoc { try { - return _languageDebugInfo.GetLocationOfName(pszName, out pbstrMkDoc, out pspanLocation); + return _languageDebugInfo.Value.GetLocationOfName(pszName, out pbstrMkDoc, out pspanLocation); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { @@ -44,7 +44,7 @@ int IVsLanguageDebugInfo.GetNameOfLocation(IVsTextBuffer pBuffer, int iLine, int { try { - return _languageDebugInfo.GetNameOfLocation(pBuffer, iLine, iCol, out pbstrName, out piLineOffset); + return _languageDebugInfo.Value.GetNameOfLocation(pBuffer, iLine, iCol, out pbstrName, out piLineOffset); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { @@ -56,7 +56,7 @@ int IVsLanguageDebugInfo.GetProximityExpressions(IVsTextBuffer pBuffer, int iLin { try { - return _languageDebugInfo.GetProximityExpressions(pBuffer, iLine, iCol, cLines, out ppEnum); + return _languageDebugInfo.Value.GetProximityExpressions(pBuffer, iLine, iCol, cLines, out ppEnum); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { @@ -68,7 +68,7 @@ int IVsLanguageDebugInfo.IsMappedLocation(IVsTextBuffer pBuffer, int iLine, int { try { - return _languageDebugInfo.IsMappedLocation(pBuffer, iLine, iCol); + return _languageDebugInfo.Value.IsMappedLocation(pBuffer, iLine, iCol); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { @@ -80,7 +80,7 @@ int IVsLanguageDebugInfo.ResolveName(string pszName, uint dwFlags, out IVsEnumDe { try { - return _languageDebugInfo.ResolveName(pszName, dwFlags, out ppNames); + return _languageDebugInfo.Value.ResolveName(pszName, dwFlags, out ppNames); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { @@ -92,7 +92,7 @@ int IVsLanguageDebugInfo.ValidateBreakpointLocation(IVsTextBuffer pBuffer, int i { try { - return _languageDebugInfo.ValidateBreakpointLocation(pBuffer, iLine, iCol, pCodeSpan); + return _languageDebugInfo.Value.ValidateBreakpointLocation(pBuffer, iLine, iCol, pCodeSpan); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs index 4f3ce55b5f0..142575f7d46 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.TextManager.Interop; using Microsoft.VisualStudio.Utilities; using Roslyn.Utilities; @@ -41,7 +42,8 @@ public int Format(IVsTextLayer textLayer, TextSpan[] selections) private int FormatWorker(IVsTextLayer textLayer, TextSpan[] selections, CancellationToken cancellationToken) { - var textBuffer = this.EditorAdaptersFactoryService.GetDataBuffer((IVsTextBuffer)textLayer); + var editorAdaptersFactoryService = this.Package.ComponentModel.GetService(); + var textBuffer = editorAdaptersFactoryService.GetDataBuffer((IVsTextBuffer)textLayer); if (textBuffer == null) { return VSConstants.E_UNEXPECTED; @@ -56,7 +58,8 @@ private int FormatWorker(IVsTextLayer textLayer, TextSpan[] selections, Cancella var documentSyntax = ParsedDocument.CreateSynchronously(document, cancellationToken); var text = documentSyntax.Text; var root = documentSyntax.Root; - var formattingOptions = textBuffer.GetSyntaxFormattingOptions(EditorOptionsService, document.Project.GetFallbackAnalyzerOptions(), document.Project.Services, explicitFormat: true); + var editorOptionsService = this.Package.ComponentModel.GetService(); + var formattingOptions = textBuffer.GetSyntaxFormattingOptions(editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), document.Project.Services, explicitFormat: true); var ts = selections.Single(); var start = text.Lines[ts.iStartLine].Start + ts.iStartIndex; @@ -65,7 +68,7 @@ private int FormatWorker(IVsTextLayer textLayer, TextSpan[] selections, Cancella // Since we know we are on the UI thread, lets get the base indentation now, so that there is less // cleanup work to do later in Venus. - var ruleFactory = Workspace.Services.GetService(); + var ruleFactory = Workspace.Value.Services.GetService(); // use formatting that return text changes rather than tree rewrite which is more expensive var formatter = document.GetRequiredLanguageService(); diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsCodeWindowManager.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsCodeWindowManager.cs index 6bf609cd254..0029116d63c 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsCodeWindowManager.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsCodeWindowManager.cs @@ -77,7 +77,8 @@ private void AddOrRemoveDropdown() return; } - var textBuffer = _languageService.EditorAdaptersFactoryService.GetDataBuffer(buffer); + var editorAdaptersFactoryService = _languageService.Package.ComponentModel.GetService(); + var textBuffer = editorAdaptersFactoryService.GetDataBuffer(buffer); var document = textBuffer?.AsTextContainer()?.GetRelatedDocuments().FirstOrDefault(); // TODO - Remove the TS check once they move the liveshare navbar to LSP. Then we can also switch to LSP // for the local navbar implementation. @@ -155,8 +156,9 @@ private void AddDropdownBar(IVsDropdownBarManager dropdownManager) return; } - var navigationBarClient = new NavigationBarClient(dropdownManager, _codeWindow, _languageService.SystemServiceProvider, _languageService.Workspace); - var textBuffer = _languageService.EditorAdaptersFactoryService.GetDataBuffer(buffer); + var navigationBarClient = new NavigationBarClient(dropdownManager, _codeWindow, _languageService.SystemServiceProvider, _languageService.Workspace.Value); + var editorAdaptersFactoryService = _languageService.Package.ComponentModel.GetService(); + var textBuffer = editorAdaptersFactoryService.GetDataBuffer(buffer); var controllerFactoryService = _languageService.Package.ComponentModel.GetService(); var newController = controllerFactoryService.CreateController(navigationBarClient, textBuffer!); var hr = dropdownManager.AddDropdownBar(cCombos: 3, pClient: navigationBarClient); diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsLanguageDebugInfo.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsLanguageDebugInfo.cs index 1e3e8b50c8b..8bf2f54f553 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsLanguageDebugInfo.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsLanguageDebugInfo.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; +using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.LanguageServices.Implementation.Extensions; using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; using Microsoft.VisualStudio.Utilities; @@ -92,7 +93,9 @@ public int GetNameOfLocation(IVsTextBuffer pBuffer, int iLine, int iCol, out str showProgress: false); var cancellationToken = waitContext.UserCancellationToken; - var textBuffer = _languageService.EditorAdaptersFactoryService.GetDataBuffer(pBuffer); + var editorAdaptersFactoryService = _languageService.Package.ComponentModel.GetService(); + + var textBuffer = editorAdaptersFactoryService.GetDataBuffer(pBuffer); if (textBuffer == null) return default; @@ -135,7 +138,9 @@ public int GetProximityExpressions(IVsTextBuffer pBuffer, int iLine, int iCol, i if (_proximityExpressionsService == null) return null; - var textBuffer = _languageService.EditorAdaptersFactoryService.GetDataBuffer(pBuffer); + var editorAdaptersFactoryService = _languageService.Package.ComponentModel.GetService(); + + var textBuffer = editorAdaptersFactoryService.GetDataBuffer(pBuffer); if (textBuffer == null) return null; @@ -195,7 +200,7 @@ public int ResolveName(string? pszName, uint dwFlags, out IVsEnumDebugName? ppNa var cancellationToken = waitContext.UserCancellationToken; if (dwFlags == (uint)RESOLVENAMEFLAGS.RNF_BREAKPOINT) { - var solution = _languageService.Workspace.CurrentSolution; + var solution = _languageService.Workspace.Value.CurrentSolution; if (_breakpointService != null) { @@ -220,7 +225,7 @@ IVsDebugName CreateDebugName( // and using the blocked thread whenever possible. var document = breakpoint.Document; - var filePath = _languageService.Workspace.GetFilePath(document.Id); + var filePath = _languageService.Workspace.Value.GetFilePath(document.Id); // We're (unfortunately) blocking the UI thread here. So avoid async io as we actually // awant the IO to complete as quickly as possible, on this thread if necessary. @@ -264,7 +269,8 @@ private async Task ValidateBreakpointLocationAsync( if (_breakpointService == null) return VSConstants.E_FAIL; - var textBuffer = _languageService.EditorAdaptersFactoryService.GetDataBuffer(pBuffer); + var editorAdaptersFactoryService = _languageService.Package.ComponentModel.GetService(); + var textBuffer = editorAdaptersFactoryService.GetDataBuffer(pBuffer); if (textBuffer != null) { var snapshot = textBuffer.CurrentSnapshot; diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.cs index 6044e5dc322..f145d4962f0 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.cs @@ -36,39 +36,9 @@ internal abstract partial class AbstractLanguageService - /// Whether or not we have been set up. This is set once everything is wired up and cleared once tear down has begun. - /// - /// - /// We don't set this until we've completed setup. If something goes sideways during it, we will never register - /// with the shell and thus have a floating thing around that can't be safely shut down either. We're in a bad - /// state but trying to proceed will only make things worse. - /// - private bool _isSetUp; + private readonly Lazy _languageDebugInfo; + + internal readonly Lazy Workspace; protected abstract string ContentTypeName { get; } protected abstract string LanguageName { get; } @@ -81,10 +51,9 @@ protected AbstractLanguageService(TPackage package) Debug.Assert(!this.Package.JoinableTaskFactory.Context.IsOnMainThread, "Language service should be instantiated on background thread"); - this.EditorOptionsService = this.Package.ComponentModel.GetService(); - this.Workspace = this.Package.ComponentModel.GetService(); - this.EditorAdaptersFactoryService = this.Package.ComponentModel.GetService(); - this._languageDebugInfo = CreateLanguageDebugInfo(); + this.Workspace = this.Package.ComponentModel.DefaultExportProvider.GetExport(); + + this._languageDebugInfo = new Lazy(CreateLanguageDebugInfo); } private IThreadingContext ThreadingContext => this.Package.ComponentModel.GetService(); @@ -92,61 +61,26 @@ protected AbstractLanguageService(TPackage package) public override IServiceProvider SystemServiceProvider => Package; - /// - /// Setup and TearDown go in reverse order. - /// - public async Task SetupAsync(CancellationToken cancellationToken) + public void Setup(CancellationToken cancellationToken) { - // First, acquire any services we need throughout our lifetime. - // This method should only contain calls to acquire services off of the component model - // or service providers. Anything else which is more complicated should go in Initialize - // instead. - // Start off a background task to prime some components we'll need for editing. Task.Run(() => { - var formatter = this.Workspace.Services.GetLanguageServices(RoslynLanguageName).GetService(); + var formatter = this.Workspace.Value.Services.GetLanguageServices(RoslynLanguageName).GetService(); formatter?.GetDefaultFormattingRules(); }, cancellationToken).Forget(); - - await this.Package.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - - // Creating the com aggregate has to happen on the UI thread. - this.ComAggregate = Interop.ComAggregate.CreateAggregatedObject(this); - - _isSetUp = true; - } - - internal void TearDown() - { - if (!_isSetUp) - { - throw new InvalidOperationException(); - } - - _isSetUp = false; - GC.SuppressFinalize(this); - } - - ~AbstractLanguageService() - { - if (!Environment.HasShutdownStarted && _isSetUp) - { - throw new InvalidOperationException("TearDown not called!"); - } } protected virtual void SetupNewTextView(IVsTextView textView) { Contract.ThrowIfNull(textView); - var wpfTextView = EditorAdaptersFactoryService.GetWpfTextView(textView); + var editorAdaptersFactoryService = this.Package.ComponentModel.GetService(); + var wpfTextView = editorAdaptersFactoryService.GetWpfTextView(textView); Contract.ThrowIfNull(wpfTextView, "Could not get IWpfTextView for IVsTextView"); Debug.Assert(!wpfTextView.Properties.ContainsProperty(typeof(AbstractVsTextViewFilter))); - var workspace = Package.ComponentModel.GetService(); - // The lifetime of CommandFilter is married to the view wpfTextView.GetOrCreateAutoClosingProperty(v => new StandaloneCommandFilter( @@ -219,7 +153,7 @@ private void ConditionallyCollapseOutliningRegions(IVsTextView textView, IWpfTex private VsLanguageDebugInfo CreateLanguageDebugInfo() { - var languageServices = this.Workspace.Services.GetLanguageServices(RoslynLanguageName); + var languageServices = this.Workspace.Value.Services.GetLanguageServices(RoslynLanguageName); return new VsLanguageDebugInfo( this.DebuggerLanguageId, @@ -235,7 +169,7 @@ protected virtual IVsContainedLanguage CreateContainedLanguage( return new ContainedLanguage( bufferCoordinator, this.Package.ComponentModel, - this.Workspace, + this.Workspace.Value, project.Id, project, this.LanguageServiceId); diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs index 4cfe2ae2473..ddb4ffdd61f 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs @@ -24,8 +24,6 @@ internal abstract partial class AbstractPackage : Ab where TPackage : AbstractPackage where TLanguageService : AbstractLanguageService { - private TLanguageService? _languageService; - private PackageInstallerService? _packageInstallerService; private VisualStudioSymbolSearchService? _symbolSearchService; private IVsShell? _shell; @@ -70,12 +68,26 @@ private Task PackageInitializationBackgroundThreadAsync(PackageLoadTasks package // Ensure we're on the BG when creating the language service. await TaskScheduler.Default; - // Create the language service, tell it to set itself up, then store it in a field - // so we can notify it that it's time to clean up. - _languageService = CreateLanguageService(); - await _languageService.SetupAsync(cancellationToken).ConfigureAwait(false); - - return _languageService.ComAggregate!; + var languageService = CreateLanguageService(); + languageService.Setup(cancellationToken); + + // DevDiv 753309: + // We've redefined some VS interfaces that had incorrect PIAs. When + // we interop with native parts of VS, they always QI, so everything + // works. However, Razor is now managed, but assumes that the C# + // language service is native. When setting breakpoints, they + // get the language service from its GUID and cast it to IVsLanguageDebugInfo. + // This would QI the native lang service. Since we're managed and + // we've redefined IVsLanguageDebugInfo, the cast + // fails. To work around this, we put the LS inside a ComAggregate object, + // which always force a QueryInterface and allow their cast to succeed. + // + // This also fixes 752331, which is a similar problem with the + // exception assistant. + + // Creating the com aggregate has to happen on the UI thread. + await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + return Interop.ComAggregate.CreateAggregatedObject(languageService); }); // Misc workspace has to be up and running by the time our package is usable so that it can track running @@ -136,7 +148,6 @@ protected override async Task LoadComponentsAsync(CancellationToken cancellation protected abstract IEnumerable CreateEditorFactories(); protected abstract TLanguageService CreateLanguageService(); - // When registering a language service, we need to take its ComAggregate wrapper. protected void RegisterLanguageService(Type t, Func> serviceCreator) => AddService(t, async (container, cancellationToken, type) => await serviceCreator(cancellationToken).ConfigureAwait(true), promote: true); @@ -150,13 +161,6 @@ protected override void Dispose(bool disposing) { UnregisterObjectBrowserLibraryManager(); } - - // If we've created the language service then tell it it's time to clean itself up now. - if (_languageService != null) - { - _languageService.TearDown(); - _languageService = null; - } } base.Dispose(disposing); diff --git a/src/roslyn/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs b/src/roslyn/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs index dfcbf133b8a..ba5495f5eb6 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs @@ -233,7 +233,6 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"csharp_using_directive_placement", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferredUsingDirectivePlacement")}, {"dotnet_provide_date_and_time_completions", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.ProvideDateAndTimeCompletions")}, {"dotnet_log_telemetry_for_background_analyzer_execution", new FeatureFlagStorage(@"Roslyn.LogTelemetryForBackgroundAnalyzerExecution")}, - {"dotnet_lightbulb_skip_executing_deprioritized_analyzers", new FeatureFlagStorage(@"Roslyn.LightbulbSkipExecutingDeprioritizedAnalyzers")}, #pragma warning disable CS0612 // Type or member is obsolete {"dotnet_auto_xml_doc_comment_generation", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.Automatic XML Doc Comment Generation", "TextEditor.VisualBasic.Specific.AutoComment")}, #pragma warning restore diff --git a/src/roslyn/src/VisualStudio/Core/Def/PdbSourceDocument/PdbSourceDocumentOutputWindowLogger.cs b/src/roslyn/src/VisualStudio/Core/Def/PdbSourceDocument/PdbSourceDocumentOutputWindowLogger.cs index 9992c08ca69..fabda7ce6ed 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/PdbSourceDocument/PdbSourceDocumentOutputWindowLogger.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/PdbSourceDocument/PdbSourceDocumentOutputWindowLogger.cs @@ -22,7 +22,7 @@ namespace Microsoft.VisualStudio.LanguageServices.PdbSourceDocument; [Export(typeof(IPdbSourceDocumentLogger)), Shared] internal sealed class PdbSourceDocumentOutputWindowLogger : IPdbSourceDocumentLogger, IDisposable { - private static readonly Guid s_outputPaneGuid = new Guid("f543e896-2e9c-48b8-8fac-d1d5030b4b89"); + private static readonly Guid s_outputPaneGuid = new("f543e896-2e9c-48b8-8fac-d1d5030b4b89"); private IVsOutputWindowPane? _outputPane; private readonly IThreadingContext _threadingContext; diff --git a/src/roslyn/src/VisualStudio/Core/Def/PickMembers/VisualStudioPickMembersService.cs b/src/roslyn/src/VisualStudio/Core/Def/PickMembers/VisualStudioPickMembersService.cs index f4664d98806..ed9e8737d64 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/PickMembers/VisualStudioPickMembersService.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/PickMembers/VisualStudioPickMembersService.cs @@ -5,11 +5,11 @@ using System; using System.Collections.Immutable; using System.Composition; -using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PickMembers; using Microsoft.VisualStudio.Language.Intellisense; +using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.PickMembers; @@ -38,7 +38,7 @@ public PickMembersResult PickMembers( if (result == true) { return new PickMembersResult( - [.. viewModel.MemberContainers.Where(c => c.IsChecked).Select(c => c.Symbol)], + viewModel.MemberContainers.SelectAsArray(c => c.IsChecked, c => c.Symbol), options, viewModel.SelectedAll); } diff --git a/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject.cs b/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject.cs index 98232c926e5..a9a19c1665b 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject.cs @@ -53,12 +53,8 @@ internal abstract partial class AbstractLegacyProject private static readonly char[] PathSeparatorCharacters = [Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar]; - #region Mutable fields that should only be used from the UI thread - private readonly SolutionEventsBatchScopeCreator _batchScopeCreator; - #endregion - public AbstractLegacyProject( string projectSystemName, IVsHierarchy hierarchy, diff --git a/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/Legacy/SolutionEventsBatchScopeCreator.cs b/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/Legacy/SolutionEventsBatchScopeCreator.cs index dc805488e09..0816942fef4 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/Legacy/SolutionEventsBatchScopeCreator.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/Legacy/SolutionEventsBatchScopeCreator.cs @@ -4,245 +4,226 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.ComponentModel.Composition; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Editor.Shared.Extensions; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; +using Microsoft.Internal.VisualStudio.Shell.Interop; +using Microsoft.Internal.VisualStudio.Shell.ProjectSystem; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Legacy; /// -/// Creates batch scopes for projects based on IVsSolutionEvents. This is useful for projects types that don't otherwise have +/// Creates batch scopes for projects based on solution and running document table events. This is useful for projects types that don't otherwise have /// good batching concepts. /// -/// All members of this class are affinitized to the UI thread. [Export(typeof(SolutionEventsBatchScopeCreator))] -[method: ImportingConstructor] -[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class SolutionEventsBatchScopeCreator(IThreadingContext threadingContext, [Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider) +internal sealed class SolutionEventsBatchScopeCreator { + /// + /// A lock for mutating all objects in this object. This class isn't expected to have any "interesting" locking requirements, so this should just be acquired + /// in all methods. + /// + private readonly object _gate = new(); private readonly List<(ProjectSystemProject project, IVsHierarchy hierarchy, ProjectSystemProject.BatchScope batchScope)> _fullSolutionLoadScopes = []; - private readonly IThreadingContext _threadingContext = threadingContext; - private readonly IServiceProvider _serviceProvider = serviceProvider; - + /// + /// The cookie for our subscription to the running document table. Null if we're not currently subscribed. + /// private uint? _runningDocumentTableEventsCookie; private bool _isSubscribedToSolutionEvents = false; - private bool _solutionLoaded = false; - public void StartTrackingProject(ProjectSystemProject project, IVsHierarchy hierarchy) - { - _threadingContext.ThrowIfNotOnUIThread(); + private readonly IVsBackgroundSolution _backgroundSolution; + private readonly IVsRunningDocumentTable _runningDocumentTable; - EnsureSubscribedToSolutionEvents(); + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public SolutionEventsBatchScopeCreator([Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider) + { + // Fetch services we're going to need later; these are all free-threaded and cacheable on creation, and since we're only going to be + // creating this part once we're in a solution load, the services would have already been created. + _backgroundSolution = (IVsBackgroundSolution)serviceProvider.GetService(typeof(SVsBackgroundSolution)); + _runningDocumentTable = (IVsRunningDocumentTable)serviceProvider.GetService(typeof(SVsRunningDocumentTable)); + } - if (!_solutionLoaded) + public void StartTrackingProject(ProjectSystemProject project, IVsHierarchy hierarchy) + { + lock (_gate) { - _fullSolutionLoadScopes.Add((project, hierarchy, project.CreateBatchScope())); + EnsureSubscribedToSolutionEvents(); + + if (_backgroundSolution.IsSolutionOpening) + { + _fullSolutionLoadScopes.Add((project, hierarchy, project.CreateBatchScope())); - EnsureSubscribedToRunningDocumentTableEvents(); + EnsureSubscribedToRunningDocumentTableEvents(); + } } } public void StopTrackingProject(ProjectSystemProject project) { - _threadingContext.ThrowIfNotOnUIThread(); - - foreach (var scope in _fullSolutionLoadScopes) + lock (_gate) { - if (scope.project == project) + foreach (var scope in _fullSolutionLoadScopes) { - scope.batchScope.Dispose(); - _fullSolutionLoadScopes.Remove(scope); - break; + if (scope.project == project) + { + scope.batchScope.Dispose(); + _fullSolutionLoadScopes.Remove(scope); + break; + } } - } - EnsureUnsubscribedFromRunningDocumentTableEventsIfNoLongerNeeded(); + EnsureUnsubscribedFromRunningDocumentTableEventsIfNoLongerNeeded(); + } } - private void StopTrackingAllProjects() + /// + /// Closes all batch scopes for all currently tracked projects, called when the solution has finished loading. + /// + private Task StopTrackingAllProjectsAsync() { - _threadingContext.ThrowIfNotOnUIThread(); + ImmutableArray batchScopeTasks; - foreach (var (_, _, batchScope) in _fullSolutionLoadScopes) + lock (_gate) { - batchScope.Dispose(); - } + // Kick off on a background thread the work to close each of the batches. The expectation is each batch closure will fairly quickly hit the solution-level + // semaphore, so we don't need to explicitly throttle this work here. + batchScopeTasks = _fullSolutionLoadScopes.SelectAsArray(static s => Task.Run(() => s.batchScope.DisposeAsync().AsTask())); - _fullSolutionLoadScopes.Clear(); + // We always want to ensure we clear out the list and unsubscribe, even if cancellation has been requested. + _fullSolutionLoadScopes.Clear(); + + EnsureUnsubscribedFromRunningDocumentTableEventsIfNoLongerNeeded(); + } - EnsureUnsubscribedFromRunningDocumentTableEventsIfNoLongerNeeded(); + return Task.WhenAll(batchScopeTasks); } private void StopTrackingAllProjectsMatchingHierarchy(IVsHierarchy hierarchy) { - _threadingContext.ThrowIfNotOnUIThread(); - - for (var i = 0; i < _fullSolutionLoadScopes.Count; i++) + lock (_gate) { - if (_fullSolutionLoadScopes[i].hierarchy == hierarchy) + for (var i = 0; i < _fullSolutionLoadScopes.Count; i++) { - _fullSolutionLoadScopes[i].batchScope.Dispose(); - _fullSolutionLoadScopes.RemoveAt(i); - - // Go back by one so we re-check the same index - i--; + if (_fullSolutionLoadScopes[i].hierarchy == hierarchy) + { + _fullSolutionLoadScopes[i].batchScope.Dispose(); + _fullSolutionLoadScopes.RemoveAt(i); + + // Go back by one so we re-check the same index + i--; + } } - } - EnsureUnsubscribedFromRunningDocumentTableEventsIfNoLongerNeeded(); + EnsureUnsubscribedFromRunningDocumentTableEventsIfNoLongerNeeded(); + } } private void EnsureSubscribedToSolutionEvents() { - _threadingContext.ThrowIfNotOnUIThread(); - - if (_isSubscribedToSolutionEvents) + lock (_gate) { - return; - } + if (_isSubscribedToSolutionEvents) + { + return; + } - var solution = (IVsSolution)_serviceProvider.GetService(typeof(SVsSolution)); + // We never unsubscribe from these, so we just throw out the subscription. We could consider unsubscribing if/when all our + // projects are unloaded, but it seems fairly unnecessary -- it'd only be useful if somebody closed one solution but then + // opened other solutions in entirely different languages from there. + _ = _backgroundSolution.SubscribeListener(new SolutionEventsEventListener(this)); - // We never unsubscribe from these, so we just throw out the cookie. We could consider unsubscribing if/when all our - // projects are unloaded, but it seems fairly unnecessary -- it'd only be useful if somebody closed one solution but then - // opened other solutions in entirely different languages from there. - if (ErrorHandler.Succeeded(solution.AdviseSolutionEvents(new SolutionEventsEventSink(this), out _))) - { _isSubscribedToSolutionEvents = true; } - - // It's possible that we're loading after the solution has already fully loaded, so see if we missed the event - var shellMonitorSelection = (IVsMonitorSelection)_serviceProvider.GetService(typeof(SVsShellMonitorSelection)); - - if (ErrorHandler.Succeeded(shellMonitorSelection.GetCmdUIContextCookie(VSConstants.UICONTEXT.SolutionExistsAndFullyLoaded_guid, out var fullyLoadedContextCookie))) - { - if (ErrorHandler.Succeeded(shellMonitorSelection.IsCmdUIContextActive(fullyLoadedContextCookie, out var fActive)) && fActive != 0) - { - _solutionLoaded = true; - } - } } private void EnsureSubscribedToRunningDocumentTableEvents() { - _threadingContext.ThrowIfNotOnUIThread(); - - if (_runningDocumentTableEventsCookie.HasValue) + lock (_gate) { - return; - } - - var runningDocumentTable = (IVsRunningDocumentTable)_serviceProvider.GetService(typeof(SVsRunningDocumentTable)); + if (_runningDocumentTableEventsCookie.HasValue) + { + return; + } - if (ErrorHandler.Succeeded(runningDocumentTable.AdviseRunningDocTableEvents(new RunningDocumentTableEventSink(this, runningDocumentTable), out var runningDocumentTableEventsCookie))) - { - _runningDocumentTableEventsCookie = runningDocumentTableEventsCookie; + if (ErrorHandler.Succeeded(_runningDocumentTable.AdviseRunningDocTableEvents(new RunningDocumentTableEventSink(this, _runningDocumentTable), out var runningDocumentTableEventsCookie))) + { + _runningDocumentTableEventsCookie = runningDocumentTableEventsCookie; + } } } private void EnsureUnsubscribedFromRunningDocumentTableEventsIfNoLongerNeeded() { - _threadingContext.ThrowIfNotOnUIThread(); - - if (!_runningDocumentTableEventsCookie.HasValue) + lock (_gate) { - return; - } + if (!_runningDocumentTableEventsCookie.HasValue) + { + return; + } - // If we don't have any scopes left, then there is no reason to be subscribed to Running Document Table events, because - // there won't be any scopes to complete. - if (_fullSolutionLoadScopes.Count > 0) - { - return; - } + // If we don't have any scopes left, then there is no reason to be subscribed to Running Document Table events, because + // there won't be any scopes to complete. + if (_fullSolutionLoadScopes.Count > 0) + { + return; + } - var runningDocumentTable = (IVsRunningDocumentTable)_serviceProvider.GetService(typeof(SVsRunningDocumentTable)); - runningDocumentTable.UnadviseRunningDocTableEvents(_runningDocumentTableEventsCookie.Value); - _runningDocumentTableEventsCookie = null; + _runningDocumentTable.UnadviseRunningDocTableEvents(_runningDocumentTableEventsCookie.Value); + _runningDocumentTableEventsCookie = null; + } } - private sealed class SolutionEventsEventSink : IVsSolutionEvents, IVsSolutionLoadEvents + private sealed class SolutionEventsEventListener : IVsAsyncSolutionEventListener { private readonly SolutionEventsBatchScopeCreator _scopeCreator; - public SolutionEventsEventSink(SolutionEventsBatchScopeCreator scopeCreator) + public SolutionEventsEventListener(SolutionEventsBatchScopeCreator scopeCreator) => _scopeCreator = scopeCreator; - int IVsSolutionLoadEvents.OnBeforeOpenSolution(string pszSolutionFilename) + public async ValueTask OnAfterOpenSolutionAsync(AfterOpenSolutionArgs args, CancellationToken cancellationToken) { - Contract.ThrowIfTrue(_scopeCreator._fullSolutionLoadScopes.Any()); + // NOTE: the cancellationToken here might be cancelled if the user has requested that we cancel the solution load. If the cancellation happened + // prior to this method being invoked, we might see this method invoked with the token cancelled from the very start. We want to make sure + // we get rid of all the batch scopes in that case before checking the cancellation token. Thus we won't pass the token to StopTrackingAllProjectsAsync. + await _scopeCreator.StopTrackingAllProjectsAsync().WithCancellation(cancellationToken).ConfigureAwait(false); + } - _scopeCreator._solutionLoaded = false; + #region Unimplemented Members - return VSConstants.S_OK; + public void OnUnhandledException(Exception exception) + { } - int IVsSolutionEvents.OnBeforeCloseSolution(object pUnkReserved) + public ValueTask OnBeforeOpenSolutionAsync(BeforeOpenSolutionArgs args, CancellationToken cancellationToken) { - _scopeCreator._solutionLoaded = false; - - return VSConstants.S_OK; + return ValueTask.CompletedTask; } - int IVsSolutionEvents.OnAfterOpenSolution(object pUnkReserved, int fNewSolution) + public ValueTask OnBeforeCloseSolutionAsync(BeforeCloseSolutionArgs args, CancellationToken cancellationToken) { - _scopeCreator._solutionLoaded = true; - _scopeCreator.StopTrackingAllProjects(); - - return VSConstants.S_OK; + return ValueTask.CompletedTask; } - #region Unimplemented Members - - int IVsSolutionLoadEvents.OnAfterBackgroundSolutionLoadComplete() - => VSConstants.E_NOTIMPL; - - int IVsSolutionEvents.OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) - => VSConstants.E_NOTIMPL; - - int IVsSolutionEvents.OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel) - => VSConstants.E_NOTIMPL; - - int IVsSolutionEvents.OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved) - => VSConstants.E_NOTIMPL; - - int IVsSolutionEvents.OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) - => VSConstants.E_NOTIMPL; - - int IVsSolutionEvents.OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel) - => VSConstants.E_NOTIMPL; - - int IVsSolutionEvents.OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy) - => VSConstants.E_NOTIMPL; - - int IVsSolutionEvents.OnQueryCloseSolution(object pUnkReserved, ref int pfCancel) - => VSConstants.E_NOTIMPL; - - int IVsSolutionEvents.OnAfterCloseSolution(object pUnkReserved) - => VSConstants.E_NOTIMPL; - - int IVsSolutionLoadEvents.OnBeforeBackgroundSolutionLoadBegins() - => VSConstants.E_NOTIMPL; - - int IVsSolutionLoadEvents.OnQueryBackgroundLoadProjectBatch(out bool pfShouldDelayLoadToNextIdle) + public ValueTask OnAfterCloseSolutionAsync(AfterCloseSolutionArgs args, CancellationToken cancellationToken) { - pfShouldDelayLoadToNextIdle = false; - return VSConstants.E_NOTIMPL; + return ValueTask.CompletedTask; } - int IVsSolutionLoadEvents.OnBeforeLoadProjectBatch(bool fIsBackgroundIdleBatch) - => VSConstants.E_NOTIMPL; - - int IVsSolutionLoadEvents.OnAfterLoadProjectBatch(bool fIsBackgroundIdleBatch) - => VSConstants.E_NOTIMPL; + public ValueTask OnAfterRenameSolutionAsync(AfterRenameSolutionArgs args, CancellationToken cancellationToken) + { + return ValueTask.CompletedTask; + } #endregion } diff --git a/src/roslyn/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClient.cs b/src/roslyn/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClient.cs index 3afa348c505..5a83164d7a4 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClient.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClient.cs @@ -1191,7 +1191,7 @@ public void Clear() } internal TestAccessor GetTestAccessor() - => new TestAccessor(this); + => new(this); internal readonly struct TestAccessor(SnippetExpansionClient instance) { diff --git a/src/roslyn/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs b/src/roslyn/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs index 37d0865b8bb..f11d161783a 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs @@ -484,7 +484,7 @@ private async Task>> Ge var uniqueDiagnosticIds = group.SelectMany(kvp => kvp.Value.Select(d => d.Id)).ToImmutableHashSet(); var diagnosticService = _workspace.Services.GetRequiredService(); var latestProjectDiagnostics = (await diagnosticService.GetDiagnosticsForIdsAsync( - project, documentIds: default, diagnosticIds: uniqueDiagnosticIds, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, cancellationToken) + project, documentIds: default, diagnosticIds: uniqueDiagnosticIds, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, cancellationToken) .ConfigureAwait(false)).Where(IsDocumentDiagnostic); latestDocumentDiagnosticsMap.Clear(); @@ -563,7 +563,7 @@ private async Task>> Get var uniqueDiagnosticIds = diagnostics.Select(d => d.Id).ToImmutableHashSet(); var diagnosticService = _workspace.Services.GetRequiredService(); var latestDiagnosticsFromDiagnosticService = (await diagnosticService.GetDiagnosticsForIdsAsync( - project, documentIds: default, diagnosticIds: uniqueDiagnosticIds, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, cancellationToken) + project, documentIds: default, diagnosticIds: uniqueDiagnosticIds, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, cancellationToken) .ConfigureAwait(false)); latestDiagnosticsToFix.Clear(); diff --git a/src/roslyn/src/VisualStudio/Core/Def/Telemetry/CodeMarkerLogger.cs b/src/roslyn/src/VisualStudio/Core/Def/Telemetry/CodeMarkerLogger.cs index b146356ef20..00e906eeb3d 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/Telemetry/CodeMarkerLogger.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/Telemetry/CodeMarkerLogger.cs @@ -13,10 +13,10 @@ namespace Microsoft.VisualStudio.LanguageServices.Telemetry; internal sealed class CodeMarkerLogger : ILogger { - public static readonly CodeMarkerLogger Instance = new CodeMarkerLogger(); + public static readonly CodeMarkerLogger Instance = new(); private static readonly Dictionary>> s_blockMap - = new Dictionary>>() + = new() { { FunctionId.NavigateTo_Search, new List>() { @@ -111,7 +111,7 @@ private static readonly Dictionary> s_map - = new Dictionary>() + = new() { { FunctionId.Rename_InlineSession, new List() { CodeMarkerEvent.perfVBRenameSymbolEnd } }, { FunctionId.BackgroundCompiler_BuildCompilationsAsync, new List() { CodeMarkerEvent.perfVBCompilerReachedBoundState, CodeMarkerEvent.perfVBCompilerReachedCompiledState } }, diff --git a/src/roslyn/src/VisualStudio/Core/Def/Utilities/ClipboardHelpers.cs b/src/roslyn/src/VisualStudio/Core/Def/Utilities/ClipboardHelpers.cs index 4c760947edc..8a4046c1cdf 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/Utilities/ClipboardHelpers.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/Utilities/ClipboardHelpers.cs @@ -92,7 +92,7 @@ internal static class ClipboardHelpers } private static FORMATETC CreateFormatEtc(ushort format) - => new FORMATETC + => new() { cfFormat = format, ptd = IntPtr.Zero, diff --git a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs index a9131b1c901..3407e12c36f 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs @@ -38,8 +38,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel; internal abstract partial class AbstractCodeModelService : ICodeModelService { - private readonly ConditionalWeakTable> _treeToNodeKeyMaps = - new ConditionalWeakTable>(); + private readonly ConditionalWeakTable> _treeToNodeKeyMaps = new(); protected readonly ISyntaxFactsService SyntaxFactsService; diff --git a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/CodeModelExtensions.cs b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/CodeModelExtensions.cs index 32a59be00e2..92d28022732 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/CodeModelExtensions.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/CodeModelExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel; internal static class CodeModelExtensions { private static readonly SymbolDisplayFormat s_fullNameWithEscapedKeywordsFormat = - new SymbolDisplayFormat( + new( globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted, typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, propertyStyle: SymbolDisplayPropertyStyle.NameOnly, diff --git a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/CodeModelProjectCache.cs b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/CodeModelProjectCache.cs index c4c4058e505..3b620d941a6 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/CodeModelProjectCache.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/CodeModelProjectCache.cs @@ -23,8 +23,8 @@ internal sealed partial class CodeModelProjectCache private readonly ProjectId _projectId; private readonly ICodeModelInstanceFactory _codeModelInstanceFactory; - private readonly Dictionary _cache = new Dictionary(StringComparer.OrdinalIgnoreCase); - private readonly object _cacheGate = new object(); + private readonly Dictionary _cache = new(StringComparer.OrdinalIgnoreCase); + private readonly object _cacheGate = new(); private EnvDTE.CodeModel? _rootCodeModel; private bool _zombied; diff --git a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/InternalElements/CodeClass.cs b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/InternalElements/CodeClass.cs index 7988429afd6..43cdf3c4641 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/InternalElements/CodeClass.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/InternalElements/CodeClass.cs @@ -17,7 +17,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.Inter public sealed class CodeClass : AbstractCodeType, EnvDTE.CodeClass, EnvDTE80.CodeClass2, ICodeClassBase { private static readonly SymbolDisplayFormat s_BaseNameFormat = - new SymbolDisplayFormat( + new( typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, memberOptions: SymbolDisplayMemberOptions.IncludeContainingType, diff --git a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.AttributeInfo.cs b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.AttributeInfo.cs index 9231bdff645..0196136da31 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.AttributeInfo.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.AttributeInfo.cs @@ -10,7 +10,7 @@ internal abstract partial class AbstractMethodXmlBuilder { private readonly struct AttributeInfo { - public static readonly AttributeInfo Empty = new AttributeInfo(); + public static readonly AttributeInfo Empty = new(); public readonly string Name; public readonly string Value; diff --git a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.cs b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.cs index 7ded0b68a93..d005c9f19f8 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.cs @@ -192,7 +192,7 @@ private static AttributeInfo ImplicitAttribute(bool? @implicit) } private static AttributeInfo LineNumberAttribute(int lineNumber) - => new AttributeInfo(LineAttributeName, lineNumber.ToString()); + => new(LineAttributeName, lineNumber.ToString()); private static AttributeInfo NameAttribute(string name) { @@ -205,7 +205,7 @@ private static AttributeInfo NameAttribute(string name) } private static AttributeInfo RankAttribute(int rank) - => new AttributeInfo(RankAttributeName, rank.ToString()); + => new(RankAttributeName, rank.ToString()); private static AttributeInfo SpecialCastKindAttribute(SpecialCastKind? specialCastKind = null) => specialCastKind switch diff --git a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModel.cs b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModel.cs index 56301cb6fa7..a460160331f 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModel.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModel.cs @@ -18,7 +18,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel; /// internal sealed class ProjectCodeModel : IProjectCodeModel { - private readonly NonReentrantLock _guard = new NonReentrantLock(); + private readonly NonReentrantLock _guard = new(); private readonly IThreadingContext _threadingContext; private readonly ProjectId _projectId; private readonly ICodeModelInstanceFactory _codeModelInstanceFactory; diff --git a/src/roslyn/src/VisualStudio/Core/Impl/Options/AbstractOptionPreviewViewModel.cs b/src/roslyn/src/VisualStudio/Core/Impl/Options/AbstractOptionPreviewViewModel.cs index 362002eb2ba..6bc33157b58 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/Options/AbstractOptionPreviewViewModel.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/Options/AbstractOptionPreviewViewModel.cs @@ -234,8 +234,8 @@ protected void AddUnusedParameterOption(OptionStore optionStore, string title, s { var unusedParameterPreferences = new List { - new CodeStylePreference(ServicesVSResources.Non_public_methods, isChecked: false), - new CodeStylePreference(ServicesVSResources.All_methods, isChecked: true), + new(ServicesVSResources.Non_public_methods, isChecked: false), + new(ServicesVSResources.All_methods, isChecked: true), }; var enumValues = new[] diff --git a/src/roslyn/src/VisualStudio/Core/Impl/Options/CodeStyleNoticeTextBlock.xaml.cs b/src/roslyn/src/VisualStudio/Core/Impl/Options/CodeStyleNoticeTextBlock.xaml.cs index 6e62f172c51..298c24354fe 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/Options/CodeStyleNoticeTextBlock.xaml.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/Options/CodeStyleNoticeTextBlock.xaml.cs @@ -18,7 +18,7 @@ internal partial class CodeStyleNoticeTextBlock : TextBlock public CodeStyleNoticeTextBlock() => InitializeComponent(); - public static readonly Uri CodeStylePageHeaderLearnMoreUri = new Uri(UseEditorConfigUrl); + public static readonly Uri CodeStylePageHeaderLearnMoreUri = new(UseEditorConfigUrl); public static string CodeStylePageHeader => ServicesVSResources.Code_style_header_use_editor_config; public static string CodeStylePageHeaderLearnMoreText => ServicesVSResources.Learn_more; diff --git a/src/roslyn/src/VisualStudio/Core/Impl/Options/GridOptionPreviewControl.xaml.cs b/src/roslyn/src/VisualStudio/Core/Impl/Options/GridOptionPreviewControl.xaml.cs index 6538e6ae7f8..72647c3a456 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/Options/GridOptionPreviewControl.xaml.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/Options/GridOptionPreviewControl.xaml.cs @@ -29,7 +29,7 @@ internal partial class GridOptionPreviewControl : AbstractOptionPageControl private readonly IEnumerable<(string feature, ImmutableArray options)> _groupedEditorConfigOptions; private readonly string _language; - public static readonly Uri CodeStylePageHeaderLearnMoreUri = new Uri(UseEditorConfigUrl); + public static readonly Uri CodeStylePageHeaderLearnMoreUri = new(UseEditorConfigUrl); public static string CodeStylePageHeader => ServicesVSResources.Code_style_header_use_editor_config; public static string CodeStylePageHeaderLearnMoreText => ServicesVSResources.Learn_more; public static string DescriptionHeader => ServicesVSResources.Description; diff --git a/src/roslyn/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingStyleOptionPageViewModel.cs b/src/roslyn/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingStyleOptionPageViewModel.cs index 8d0bd78469f..a53e99877a0 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingStyleOptionPageViewModel.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingStyleOptionPageViewModel.cs @@ -13,6 +13,7 @@ using Microsoft.VisualStudio.Imaging; using Microsoft.VisualStudio.LanguageServices.Implementation.Options.Style.NamingPreferences; using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; +using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options.Style; @@ -97,9 +98,9 @@ internal void UpdateSpecificationList(ManageSymbolSpecificationsDialogViewModel var symbolSpecifications = viewModel.Items.Cast().Select(n => new SymbolSpecification( n.ID, n.ItemName, - [.. n.SymbolKindList.Where(s => s.IsChecked).Select(k => k.CreateSymbolOrTypeOrMethodKind())], - [.. n.AccessibilityList.Where(s => s.IsChecked).Select(a => a._accessibility)], - [.. n.ModifierList.Where(s => s.IsChecked).Select(m => new SymbolSpecification.ModifierKind(m._modifier.Modifiers))])); + n.SymbolKindList.SelectAsArray(s => s.IsChecked, k => k.CreateSymbolOrTypeOrMethodKind()), + n.AccessibilityList.SelectAsArray(s => s.IsChecked, a => a._accessibility), + n.ModifierList.SelectAsArray(s => s.IsChecked, m => new SymbolSpecification.ModifierKind(m._modifier.Modifiers)))); Specifications.Clear(); foreach (var specification in symbolSpecifications) diff --git a/src/roslyn/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/SymbolSpecification/SymbolSpecificationViewModel.cs b/src/roslyn/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/SymbolSpecification/SymbolSpecificationViewModel.cs index 2d003039619..2ae34d4b071 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/SymbolSpecification/SymbolSpecificationViewModel.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/SymbolSpecification/SymbolSpecificationViewModel.cs @@ -144,9 +144,9 @@ internal SymbolSpecification GetSymbolSpecification() return new SymbolSpecification( ID, ItemName, - [.. SymbolKindList.Where(s => s.IsChecked).Select(s => s.CreateSymbolOrTypeOrMethodKind())], - [.. AccessibilityList.Where(a => a.IsChecked).Select(a => a._accessibility)], - [.. ModifierList.Where(m => m.IsChecked).Select(m => new ModifierKind(m._modifier.Modifiers))]); + SymbolKindList.SelectAsArray(s => s.IsChecked, s => s.CreateSymbolOrTypeOrMethodKind()), + AccessibilityList.SelectAsArray(a => a.IsChecked, a => a._accessibility), + ModifierList.SelectAsArray(m => m.IsChecked, m => new ModifierKind(m._modifier.Modifiers))); } internal bool TrySubmit() diff --git a/src/roslyn/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb b/src/roslyn/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb index 0e574edcfeb..4afb4474e10 100644 --- a/src/roslyn/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb +++ b/src/roslyn/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb @@ -240,7 +240,7 @@ class { } ' confirm that IDE doesn't report the diagnostics Dim diagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( - document.Project, ImmutableArray.Create(document.Id), diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + document.Project, ImmutableArray.Create(document.Id), diagnosticIds:=Nothing, AnalyzerFilter.All, includeLocalDocumentDiagnostics:=True, CancellationToken.None) Assert.False(diagnostics.Any()) End Using diff --git a/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Editor/Implementation/Debugging/FSharpBreakpointResolutionResult.cs b/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Editor/Implementation/Debugging/FSharpBreakpointResolutionResult.cs index 8be52df7103..c290086e118 100644 --- a/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Editor/Implementation/Debugging/FSharpBreakpointResolutionResult.cs +++ b/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Editor/Implementation/Debugging/FSharpBreakpointResolutionResult.cs @@ -21,8 +21,8 @@ private FSharpBreakpointResolutionResult(BreakpointResolutionResult result) public bool IsLineBreakpoint => UnderlyingObject.IsLineBreakpoint; public static FSharpBreakpointResolutionResult CreateSpanResult(Document document, TextSpan textSpan, string? locationNameOpt = null) - => new FSharpBreakpointResolutionResult(BreakpointResolutionResult.CreateSpanResult(document, textSpan, locationNameOpt)); + => new(BreakpointResolutionResult.CreateSpanResult(document, textSpan, locationNameOpt)); public static FSharpBreakpointResolutionResult CreateLineResult(Document document, string? locationNameOpt = null) - => new FSharpBreakpointResolutionResult(BreakpointResolutionResult.CreateLineResult(document, locationNameOpt)); + => new(BreakpointResolutionResult.CreateLineResult(document, locationNameOpt)); } diff --git a/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpSimplifyNameDiagnosticAnalyzer.cs b/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpSimplifyNameDiagnosticAnalyzer.cs index 100cd2c1df0..98398188903 100644 --- a/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpSimplifyNameDiagnosticAnalyzer.cs +++ b/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpSimplifyNameDiagnosticAnalyzer.cs @@ -39,7 +39,7 @@ public Task> AnalyzeSemanticsAsync(DiagnosticDescript internal class FSharpSimplifyNameDiagnosticAnalyzer : DocumentDiagnosticAnalyzer, IBuiltInAnalyzer { private readonly DiagnosticDescriptor _descriptor = - new DiagnosticDescriptor( + new( IDEDiagnosticIds.SimplifyNamesDiagnosticId, ExternalAccessFSharpResources.SimplifyName, ExternalAccessFSharpResources.NameCanBeSimplified, diff --git a/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedDeclarationsAnalyzer.cs b/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedDeclarationsAnalyzer.cs index 95ee909b74c..bde4d5ba30a 100644 --- a/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedDeclarationsAnalyzer.cs +++ b/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedDeclarationsAnalyzer.cs @@ -41,7 +41,7 @@ internal class FSharpUnusedDeclarationsDiagnosticAnalyzer : DocumentDiagnosticAn private const string DescriptorId = IDEDiagnosticIds.ValueAssignedIsUnusedDiagnosticId; private readonly DiagnosticDescriptor _descriptor = - new DiagnosticDescriptor( + new( DescriptorId, ExternalAccessFSharpResources.TheValueIsUnused, ExternalAccessFSharpResources.TheValueIsUnused, diff --git a/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedOpensDiagnosticAnalyzer.cs b/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedOpensDiagnosticAnalyzer.cs index fcb8558fcd5..71267b85282 100644 --- a/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedOpensDiagnosticAnalyzer.cs +++ b/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedOpensDiagnosticAnalyzer.cs @@ -39,7 +39,7 @@ public Task> AnalyzeSemanticsAsync(DiagnosticDescript internal class FSharpUnusedOpensDeclarationsDiagnosticAnalyzer : DocumentDiagnosticAnalyzer { private readonly DiagnosticDescriptor _descriptor = - new DiagnosticDescriptor( + new( IDEDiagnosticIds.RemoveUnnecessaryImportsDiagnosticId, ExternalAccessFSharpResources.RemoveUnusedOpens, ExternalAccessFSharpResources.UnusedOpens, diff --git a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs index 3636689ba17..af2ae5490e0 100644 --- a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs +++ b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs @@ -580,18 +580,18 @@ public async Task IsNavigationBarEnabledAsync(CancellationToken cancellati return (await GetNavigationBarMarginAsync(view, cancellationToken)) is not null; } - private async Task> GetNavigationBarComboBoxesAsync(IWpfTextView textView, CancellationToken cancellationToken) + private async Task> GetNavigationBarComboBoxesAsync(IWpfTextView textView, CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); var margin = await GetNavigationBarMarginAsync(textView, cancellationToken); try { - return margin.GetFieldValue>("_combos"); + return margin.GetFieldValue>("_combos"); } catch (FieldAccessException) { - return margin.GetFieldValue>("Combos"); + return margin.GetFieldValue>("Combos"); } } @@ -842,13 +842,12 @@ private Func> GetLightBulbApplicatio action = fixAllAction; if (willBlockUntilComplete - && action is AbstractFixAllSuggestedAction fixAllSuggestedAction - && fixAllSuggestedAction.CodeAction is AbstractFixAllCodeAction fixAllCodeAction) + && action is EditorSuggestedActionForRefactorOrFixAll fixAllSuggestedAction) { // Ensure the preview changes dialog will not be shown. Since the operation 'willBlockUntilComplete', // the caller would not be able to interact with the preview changes dialog, and the tests would // either timeout or deadlock. - fixAllCodeAction.GetTestAccessor().ShowPreviewChangesDialog = false; + fixAllSuggestedAction.CodeAction.GetTestAccessor().ShowPreviewChangesDialog = false; } if (string.IsNullOrEmpty(actionName)) @@ -860,7 +859,7 @@ private Func> GetLightBulbApplicatio broker.DismissSession(view); } - if (action is not SuggestedAction suggestedAction) + if (action is not EditorSuggestedAction suggestedAction) return true; broker.DismissSession(view); @@ -933,7 +932,7 @@ private async Task> SelectActionsAsync(IEnumerable return actions; } - private async Task GetFixAllSuggestedActionAsync(IEnumerable actionSets, FixAllScope fixAllScope, CancellationToken cancellationToken) + private async Task GetFixAllSuggestedActionAsync(IEnumerable actionSets, FixAllScope fixAllScope, CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); @@ -941,13 +940,10 @@ private async Task> SelectActionsAsync(IEnumerable { foreach (var action in actionSet.Actions) { - if (action is AbstractFixAllSuggestedAction fixAllSuggestedAction) + if (action is EditorSuggestedActionForRefactorOrFixAll fixAllSuggestedAction && + fixAllSuggestedAction.CodeAction.RefactorOrFixAllState.Scope == fixAllScope) { - var fixAllCodeAction = fixAllSuggestedAction.CodeAction as AbstractFixAllCodeAction; - if (fixAllCodeAction?.FixAllState?.Scope == fixAllScope) - { - return fixAllSuggestedAction; - } + return fixAllSuggestedAction; } if (action.HasActionSets) diff --git a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs index d05be9ee625..dff4e73e3c4 100644 --- a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs +++ b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs @@ -203,12 +203,12 @@ private async Task CreateSolutionAsync(string solutionPath, string solutionName, return references; } - public async Task GetProjectReferencesAsync(string projectName, CancellationToken cancellationToken) + public async Task> GetProjectReferencesAsync(string projectName, CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); var project = await GetProjectAsync(projectName, cancellationToken); - var references = ((VSProject)project.Object).References.Cast().Where(x => x.SourceProject != null).Select(x => x.Name).ToArray(); + var references = ((VSProject)project.Object).References.Cast().SelectAsArray(x => x.SourceProject != null, x => x.Name); return references; } diff --git a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/Microsoft.VisualStudio.LanguageServices.New.IntegrationTests.csproj b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/Microsoft.VisualStudio.LanguageServices.New.IntegrationTests.csproj index 2bf4f13ced4..ae37c6137d6 100644 --- a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/Microsoft.VisualStudio.LanguageServices.New.IntegrationTests.csproj +++ b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/Microsoft.VisualStudio.LanguageServices.New.IntegrationTests.csproj @@ -54,6 +54,7 @@ + diff --git a/src/roslyn/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs b/src/roslyn/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs index bb155d68322..83b8e30733b 100644 --- a/src/roslyn/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs +++ b/src/roslyn/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs @@ -44,7 +44,7 @@ internal sealed class RemoteLanguageServiceWorkspace : CodeAnalysis.Workspace, I /// // Our usage of SemaphoreSlim is fine. We don't perform blocking waits for it on the UI thread. #pragma warning disable RS0030 // Do not use banned APIs - private static readonly SemaphoreSlim s_RemotePathsGate = new SemaphoreSlim(initialCount: 1); + private static readonly SemaphoreSlim s_RemotePathsGate = new(initialCount: 1); #pragma warning restore RS0030 // Do not use banned APIs private readonly IServiceProvider _serviceProvider; diff --git a/src/roslyn/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspaceHost.cs b/src/roslyn/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspaceHost.cs index c52b9ef3f54..f2fef6e95a3 100644 --- a/src/roslyn/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspaceHost.cs +++ b/src/roslyn/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspaceHost.cs @@ -36,7 +36,7 @@ internal sealed class RemoteLanguageServiceWorkspaceHost : ICollaborationService // A collection of loaded Roslyn Project IDs, indexed by project path. private ImmutableDictionary _loadedProjects = ImmutableDictionary.Create(StringComparer.OrdinalIgnoreCase); private ImmutableDictionary _loadedProjectInfo = ImmutableDictionary.Create(StringComparer.OrdinalIgnoreCase); - private TaskCompletionSource _projectsLoadedTaskCompletionSource = new TaskCompletionSource(); + private TaskCompletionSource _projectsLoadedTaskCompletionSource = new(); private readonly RemoteProjectInfoProvider _remoteProjectInfoProvider; private readonly SVsServiceProvider _serviceProvider; diff --git a/src/roslyn/src/VisualStudio/LiveShare/Impl/CustomProtocol/LspRequestExtensions.cs b/src/roslyn/src/VisualStudio/LiveShare/Impl/CustomProtocol/LspRequestExtensions.cs index 414dcd10196..0fd054606c7 100644 --- a/src/roslyn/src/VisualStudio/LiveShare/Impl/CustomProtocol/LspRequestExtensions.cs +++ b/src/roslyn/src/VisualStudio/LiveShare/Impl/CustomProtocol/LspRequestExtensions.cs @@ -12,7 +12,7 @@ namespace Microsoft.VisualStudio.LanguageServices.LiveShare.Protocol; public static class LspRequestExtensions { internal static LS.LspRequest ToLSRequest(this LSP.LspRequest lspRequest) - => new LS.LspRequest(lspRequest.Name); + => new(lspRequest.Name); internal static LSP.ClientCapabilities GetClientCapabilities(this LS.RequestContext requestContext) => requestContext.ClientCapabilities?.ToObject() ?? new LSP.VSInternalClientCapabilities(); diff --git a/src/roslyn/src/VisualStudio/LiveShare/Impl/LiveShareInitializeHandler.cs b/src/roslyn/src/VisualStudio/LiveShare/Impl/LiveShareInitializeHandler.cs index 4a8d2406b85..73780d3c8b3 100644 --- a/src/roslyn/src/VisualStudio/LiveShare/Impl/LiveShareInitializeHandler.cs +++ b/src/roslyn/src/VisualStudio/LiveShare/Impl/LiveShareInitializeHandler.cs @@ -15,7 +15,7 @@ namespace Microsoft.VisualStudio.LanguageServices.LiveShare.Shims; internal class LiveShareInitializeHandler : ILspRequestHandler { - private static readonly InitializeResult s_initializeResult = new InitializeResult + private static readonly InitializeResult s_initializeResult = new() { Capabilities = new ServerCapabilities { diff --git a/src/roslyn/src/VisualStudio/LiveShare/Test/ProjectsHandlerTests.cs b/src/roslyn/src/VisualStudio/LiveShare/Test/ProjectsHandlerTests.cs index 775506ade8f..608653e1fec 100644 --- a/src/roslyn/src/VisualStudio/LiveShare/Test/ProjectsHandlerTests.cs +++ b/src/roslyn/src/VisualStudio/LiveShare/Test/ProjectsHandlerTests.cs @@ -31,7 +31,7 @@ public async Task TestProjectsAsync(bool mutatingLspWorkspace) } private static CustomProtocol.Project CreateLspProject(Project project) - => new CustomProtocol.Project() + => new() { Language = project.Language, Name = project.Name, diff --git a/src/roslyn/src/VisualStudio/TestUtilities2/MockServiceProvider.vb b/src/roslyn/src/VisualStudio/TestUtilities2/MockServiceProvider.vb index 68dde06460c..4cbf4161484 100644 --- a/src/roslyn/src/VisualStudio/TestUtilities2/MockServiceProvider.vb +++ b/src/roslyn/src/VisualStudio/TestUtilities2/MockServiceProvider.vb @@ -4,6 +4,7 @@ Imports System.ComponentModel.Composition Imports Microsoft.CodeAnalysis.Host.Mef +Imports Microsoft.Internal.VisualStudio.Shell.Interop Imports Microsoft.VisualStudio.ComponentModelHost Imports Microsoft.VisualStudio.Shell Imports Microsoft.VisualStudio.Shell.Interop @@ -25,7 +26,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests Private ReadOnly _exportProvider As Composition.ExportProvider Private ReadOnly _fileChangeEx As New MockVsFileChangeEx - Public MockMonitorSelection As IVsMonitorSelection Public MockRunningDocumentTable As New MockVsRunningDocumentTable @@ -41,12 +41,12 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests Dim solutionMock As New Mock(Of IVsSolution2)(MockBehavior.Loose) Return solutionMock.Object + Case GetType(SVsBackgroundSolution) + Return GetVsBackgroundSolutionMock() + Case GetType(SComponentModel) Return GetComponentModelMock() - Case GetType(SVsShellMonitorSelection) - Return MockMonitorSelection - Case GetType(SVsXMLMemberIndexService) Return New MockXmlMemberIndexService @@ -79,6 +79,19 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests Friend Function GetComponentModelMock() As IComponentModel Return New MockComponentModel(_exportProvider) End Function + + Private Shared Function GetVsBackgroundSolutionMock() As IVsBackgroundSolution + ' Return a simple mock that lets callers subscribe to events + Dim mock = New Mock(Of IVsBackgroundSolution)(MockBehavior.Strict) + + mock.Setup(Function(s) s.SubscribeListener(It.IsAny(Of Object))).Returns( + Function() + Return New Mock(Of IVsBackgroundDisposable)().Object + End Function) + mock.SetupGet(Function(s) s.IsSolutionOpening).Returns(False) + + Return mock.Object + End Function End Class End Namespace diff --git a/src/roslyn/src/VisualStudio/TestUtilities2/ProjectSystemShim/Framework/TestEnvironment.vb b/src/roslyn/src/VisualStudio/TestUtilities2/ProjectSystemShim/Framework/TestEnvironment.vb index e2fcf746a96..5967e4786e9 100644 --- a/src/roslyn/src/VisualStudio/TestUtilities2/ProjectSystemShim/Framework/TestEnvironment.vb +++ b/src/roslyn/src/VisualStudio/TestUtilities2/ProjectSystemShim/Framework/TestEnvironment.vb @@ -85,9 +85,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Fr ThreadingContext = ExportProvider.GetExportedValue(Of IThreadingContext)() Implementation.Interop.WrapperPolicy.s_ComWrapperFactory = MockComWrapperFactory.Instance - Dim mockServiceProvider As MockServiceProvider = ExportProvider.GetExportedValue(Of MockServiceProvider)() - mockServiceProvider.MockMonitorSelection = New MockShellMonitorSelection(True) - ServiceProvider = mockServiceProvider + ServiceProvider = ExportProvider.GetExportedValue(Of MockServiceProvider)() End Sub Public ReadOnly Property ProjectFactory As VisualStudioProjectFactory @@ -170,46 +168,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Fr Return Workspace.CurrentSolution.Projects.Single().CompilationOptions End Function - Private Class MockShellMonitorSelection - Implements IVsMonitorSelection - - Public Property SolutionIsFullyLoaded As Boolean - - Public Sub New(solutionIsFullyLoaded As Boolean) - Me.SolutionIsFullyLoaded = solutionIsFullyLoaded - End Sub - - Public Function AdviseSelectionEvents(pSink As IVsSelectionEvents, ByRef pdwCookie As UInteger) As Integer Implements IVsMonitorSelection.AdviseSelectionEvents - Return VSConstants.S_OK - End Function - - Public Function GetCmdUIContextCookie( ByRef rguidCmdUI As Guid, ByRef pdwCmdUICookie As UInteger) As Integer Implements IVsMonitorSelection.GetCmdUIContextCookie - Return VSConstants.S_OK - End Function - - Public Function GetCurrentElementValue( elementid As UInteger, ByRef pvarValue As Object) As Integer Implements IVsMonitorSelection.GetCurrentElementValue - Throw New NotImplementedException() - End Function - - Public Function GetCurrentSelection(ByRef ppHier As IntPtr, ByRef pitemid As UInteger, ByRef ppMIS As IVsMultiItemSelect, ByRef ppSC As IntPtr) As Integer Implements IVsMonitorSelection.GetCurrentSelection - Throw New NotImplementedException() - End Function - - Public Function IsCmdUIContextActive( dwCmdUICookie As UInteger, ByRef pfActive As Integer) As Integer Implements IVsMonitorSelection.IsCmdUIContextActive - ' Be lazy and don't worry checking which cookie this is, since for now the VisualStudioProjectTracker only checks for one - pfActive = If(SolutionIsFullyLoaded, 1, 0) - Return VSConstants.S_OK - End Function - - Public Function SetCmdUIContext( dwCmdUICookie As UInteger, fActive As Integer) As Integer Implements IVsMonitorSelection.SetCmdUIContext - Throw New NotImplementedException() - End Function - - Public Function UnadviseSelectionEvents( dwCookie As UInteger) As Integer Implements IVsMonitorSelection.UnadviseSelectionEvents - Throw New NotImplementedException() - End Function - End Class - Friend Async Function GetFileChangeServiceAsync() As Task(Of MockVsFileChangeEx) ' Ensure we've pushed everything to the file change watcher Dim fileChangeProvider = ExportProvider.GetExportedValue(Of FileChangeWatcherProvider) diff --git a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Guids.cs b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Guids.cs index db708dc9dfb..7dc2b9df9be 100644 --- a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Guids.cs +++ b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Guids.cs @@ -16,5 +16,5 @@ internal static class GuidList public const string guidVisualStudioDiagnosticsWindowCmdSetString = "f22c2499-790a-4b6c-b0fd-b6f0491e1c9c"; public const string guidToolWindowPersistanceString = "b2da68d7-fd1c-491a-a9a0-24f597b9f56c"; - public static readonly Guid guidVisualStudioDiagnosticsWindowCmdSet = new Guid(guidVisualStudioDiagnosticsWindowCmdSetString); + public static readonly Guid guidVisualStudioDiagnosticsWindowCmdSet = new(guidVisualStudioDiagnosticsWindowCmdSetString); }; diff --git a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Loggers/OutputWindowLogger.cs b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Loggers/OutputWindowLogger.cs index 59a0f221710..58eb016c5a3 100644 --- a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Loggers/OutputWindowLogger.cs +++ b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Loggers/OutputWindowLogger.cs @@ -48,9 +48,9 @@ public void LogBlockEnd(FunctionId functionId, LogMessage logMessage, int unique private sealed class OutputPane { - private static readonly Guid s_outputPaneGuid = new Guid("BBAFF416-4AF5-41F2-9F93-91F283E43C3B"); + private static readonly Guid s_outputPaneGuid = new("BBAFF416-4AF5-41F2-9F93-91F283E43C3B"); - public static readonly OutputPane s_instance = new OutputPane(); + public static readonly OutputPane s_instance = new(); private readonly IServiceProvider _serviceProvider; private readonly IThreadingContext _threadingContext; diff --git a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryMode.cs b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryMode.cs index 12c6633d20f..e38d53f5a07 100644 --- a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryMode.cs +++ b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryMode.cs @@ -58,7 +58,7 @@ private sealed class MemoryHogger private const int MonitorDelay = 10000; // 10 seconds private readonly List _blocks = []; - private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + private readonly CancellationTokenSource _cancellationTokenSource = new(); public MemoryHogger() { diff --git a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/PerfMargin/StatusIndicator.xaml.cs b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/PerfMargin/StatusIndicator.xaml.cs index 960d1d28892..f0250e87f77 100644 --- a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/PerfMargin/StatusIndicator.xaml.cs +++ b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/PerfMargin/StatusIndicator.xaml.cs @@ -45,8 +45,8 @@ private void ActivityLevel_IsActiveChanged(object sender, EventArgs e) } private const double MinimumScale = 0.2; - private static readonly DoubleAnimation s_growAnimation = new DoubleAnimation(1.0, new Duration(TimeSpan.FromSeconds(1.0)), FillBehavior.HoldEnd); - private static readonly DoubleAnimation s_shrinkAnimation = new DoubleAnimation(0.0, new Duration(TimeSpan.FromSeconds(0.33333)), FillBehavior.HoldEnd); + private static readonly DoubleAnimation s_growAnimation = new(1.0, new Duration(TimeSpan.FromSeconds(1.0)), FillBehavior.HoldEnd); + private static readonly DoubleAnimation s_shrinkAnimation = new(0.0, new Duration(TimeSpan.FromSeconds(0.33333)), FillBehavior.HoldEnd); public void UpdateOnUIThread() { diff --git a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VenusMargin/ProjectionSpanTag.cs b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VenusMargin/ProjectionSpanTag.cs index 76b4878bfed..09014a9d240 100644 --- a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VenusMargin/ProjectionSpanTag.cs +++ b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VenusMargin/ProjectionSpanTag.cs @@ -12,7 +12,7 @@ internal sealed class ProjectionSpanTag : TextMarkerTag { public const string TagId = "ProjectionTag"; - public static readonly ProjectionSpanTag Instance = new ProjectionSpanTag(); + public static readonly ProjectionSpanTag Instance = new(); public ProjectionSpanTag() : base(TagId) diff --git a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VenusMargin/VenusMargin.cs b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VenusMargin/VenusMargin.cs index 3a208ba5c58..240b4375c65 100644 --- a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VenusMargin/VenusMargin.cs +++ b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VenusMargin/VenusMargin.cs @@ -16,7 +16,7 @@ internal sealed class VenusMargin : IWpfTextViewMargin public const string MarginName = "VenusMargin"; private readonly IProjectionBuffer _projectionBuffer; - private readonly ProjectionBufferViewModel _viewModel = new ProjectionBufferViewModel(); + private readonly ProjectionBufferViewModel _viewModel = new(); private readonly ProjectionBufferMargin _control; private bool _isDisposed = false; diff --git a/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionHandler.cs b/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionHandler.cs index 3222e5fd697..42799084a78 100644 --- a/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionHandler.cs +++ b/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionHandler.cs @@ -31,7 +31,7 @@ internal sealed class CompletionHandler : ILspServiceDocumentRequestHandler request.TextDocument; protected override VSInternalDiagnosticReport CreateReport(TextDocumentIdentifier? identifier, VSDiagnostic[]? diagnostics, string? resultId) - => new VSInternalDiagnosticReport { Diagnostics = diagnostics, ResultId = resultId }; + => new() { Diagnostics = diagnostics, ResultId = resultId }; protected override ImmutableArray GetDocuments(RequestContext context) { diff --git a/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs b/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs index f96133e81b6..234e9f33784 100644 --- a/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs +++ b/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs @@ -29,7 +29,7 @@ public WorkspacePullDiagnosticHandler( { } protected override VSInternalWorkspaceDiagnosticReport CreateReport(TextDocumentIdentifier? identifier, VSDiagnostic[]? diagnostics, string? resultId) - => new VSInternalWorkspaceDiagnosticReport { TextDocument = identifier, Diagnostics = diagnostics, ResultId = resultId }; + => new() { TextDocument = identifier, Diagnostics = diagnostics, ResultId = resultId }; /// /// Collect all the opened documents from solution. diff --git a/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/XamlProjectService.cs b/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/XamlProjectService.cs index a8f3afa25c7..c56e33016a2 100644 --- a/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/XamlProjectService.cs +++ b/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/XamlProjectService.cs @@ -35,7 +35,7 @@ internal sealed partial class XamlProjectService : IDisposable private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactory; private readonly IThreadingContext _threadingContext; private readonly Dictionary _xamlProjects = []; - private readonly ConcurrentDictionary _documentIds = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private readonly ConcurrentDictionary _documentIds = new(StringComparer.OrdinalIgnoreCase); private readonly WorkspaceEventRegistration _documentClosedHandlerDisposer; private RunningDocumentTable? _rdt; diff --git a/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/XamlTextViewCreationListener.cs b/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/XamlTextViewCreationListener.cs index c5881cb3233..d7fe48edfb0 100644 --- a/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/XamlTextViewCreationListener.cs +++ b/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/XamlTextViewCreationListener.cs @@ -23,7 +23,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml; internal sealed partial class XamlTextViewCreationListener : IWpfTextViewCreationListener { // Temporary UIConext GUID owned by the XAML language service until we can get a KnownUIContext - private static readonly Guid s_serverUIContextGuid = new Guid("39F55746-6E65-4FCF-BEC5-EC0B466EAC0F"); + private static readonly Guid s_serverUIContextGuid = new("39F55746-6E65-4FCF-BEC5-EC0B466EAC0F"); private readonly IServiceProvider _serviceProvider; private readonly XamlProjectService _projectService; diff --git a/src/roslyn/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/roslyn/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index 7ad89b49d26..6571c1f887f 100644 --- a/src/roslyn/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/roslyn/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -258,7 +258,6 @@ private static string StripExplicitInterfaceName(string name) public override SyntaxNode OperatorDeclaration(OperatorKind kind, IEnumerable? parameters = null, SyntaxNode? returnType = null, Accessibility accessibility = Accessibility.NotApplicable, DeclarationModifiers modifiers = default, IEnumerable? statements = null) { return OperatorDeclaration(GetOperatorName(kind), isImplicitConversion: kind == OperatorKind.ImplicitConversion, parameters, returnType, accessibility, modifiers, statements); - } private protected override SyntaxNode OperatorDeclaration(string operatorName, bool isImplicitConversion, IEnumerable? parameters = null, SyntaxNode? returnType = null, Accessibility accessibility = Accessibility.NotApplicable, DeclarationModifiers modifiers = default, IEnumerable? statements = null) @@ -3739,5 +3738,18 @@ internal override SyntaxNode ParseExpression(string stringToParse) internal override SyntaxNode ParseTypeName(string stringToParse) => SyntaxFactory.ParseTypeName(stringToParse); + internal override SyntaxNode ExtensionBlockDeclaration( + SyntaxNode extensionParameter, + IEnumerable? typeParameters, + IEnumerable members) + { + SyntaxList extensionMembers = [.. members.OfType().WhereNotNull()]; + var typeParameterList = AsTypeParameterList(typeParameters); + + return SyntaxFactory.ExtensionBlockDeclaration(attributeLists: default, modifiers: default, ExtensionKeyword, + typeParameterList, parameterList: AsParameterList([extensionParameter]), + constraintClauses: default, OpenBraceToken, extensionMembers, CloseBraceToken, default); + } + #endregion } diff --git a/src/roslyn/src/Workspaces/CSharpTest/CSharpCommandLineParserServiceTests.cs b/src/roslyn/src/Workspaces/CSharpTest/CSharpCommandLineParserServiceTests.cs index d1c8049b0c8..4b068b74a58 100644 --- a/src/roslyn/src/Workspaces/CSharpTest/CSharpCommandLineParserServiceTests.cs +++ b/src/roslyn/src/Workspaces/CSharpTest/CSharpCommandLineParserServiceTests.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests; public sealed class CSharpCommandLineParserServiceTests { private static readonly string s_directory = Path.GetTempPath(); - private readonly CSharpCommandLineParserService _parser = new CSharpCommandLineParserService(); + private readonly CSharpCommandLineParserService _parser = new(); private CSharpCommandLineArguments GetArguments(params string[] args) => (CSharpCommandLineArguments)_parser.Parse(args, baseDirectory: s_directory, isInteractive: false, sdkDirectory: s_directory); diff --git a/src/roslyn/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs b/src/roslyn/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs index d064441285f..ecf4f2d0c0a 100644 --- a/src/roslyn/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs +++ b/src/roslyn/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs @@ -5288,6 +5288,358 @@ public class C """); } + [Fact] + public void TestExtensionDeclaration_01() + { + var compilation = Compile(""" + public static class E + { + extension(int i) + { + public void M() + { + } + } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + public static class E : global::System.Object + { + extension(global::System.Int32 i) + { + public void M() + { + } + } + } + """); + } + + [Fact] + public void TestExtensionDeclaration_02() + { + // unnamed extension parameter + var compilation = Compile(""" + public static class E + { + extension(int) + { + public static int P { get => 0; set { } } + } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + public static class E : global::System.Object + { + extension(global::System.Int32) + { + public static global::System.Int32 P { get; set; } + } + } + """); + } + + [Fact] + public void TestExtensionDeclaration_03() + { + // generic + var compilation = Compile(""" + public static class E + { + extension(int) + { + public static int P => 0; + } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + public static class E : global::System.Object + { + extension(global::System.Int32) + { + public static global::System.Int32 P { get; } + } + } + """); + } + + [Fact] + public void TestExtensionDeclaration_04() + { + // null extension parameter + var compilation = Compile(""" + public static class E + { + extension(__arglist) + { + } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E").Single(); + Assert.Throws(() => Generator.Declaration(symbol)); + } + + [Fact] + public void TestExtensionDeclaration_05() + { + // operator + var compilation = Compile(""" + public static class E + { + extension(C) + { + public static C operator +(C c1, C c2) => null; + } + } + + class C { } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + public static class E : global::System.Object + { + extension(global::C) + { + public static global::C operator +(global::C c1, global::C c2) + { + } + } + } + """); + } + + [Fact] + public void TestExtensionDeclaration_06() + { + // in struct + var compilation = Compile(""" + public struct E + { + extension(int) + { + public void M() { } + } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + public struct E + { + extension(global::System.Int32) + { + public void M() + { + } + } + + public E() + { + } + } + """); + } + + [Fact] + public void TestExtensionDeclaration_07() + { + // in non-static class + var compilation = Compile(""" + public class E + { + extension(int) + { + public void M() { } + } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + public class E : global::System.Object + { + extension(global::System.Int32) + { + public void M() + { + } + } + + public E() + { + } + } + """); + } + + [Fact] + public void TestExtensionDeclaration_08() + { + // top-level + var compilation = Compile(""" + extension(int) + { + public void M() { } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + extension(global::System.Int32) + { + public void M() + { + } + } + """); + } + + [Fact] + public void TestExtensionDeclaration_09() + { + // nested in type + var compilation = Compile(""" + static class E1 + { + static class E2 + { + extension(int) + { + public void M() { } + } + } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E1").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + internal static class E1 : global::System.Object + { + private static class E2 : global::System.Object + { + extension(global::System.Int32) + { + public void M() + { + } + } + } + } + """); + } + + [Fact] + public void TestExtensionDeclaration_10() + { + // merged extension blocks + var compilation = Compile(""" + static class E + { + extension(int) + { + public void M1() { } + } + extension(int) + { + public void M2() { } + } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + internal static class E : global::System.Object + { + extension(global::System.Int32) + { + public void M1() + { + } + } + + extension(global::System.Int32) + { + public void M2() + { + } + } + } + """); + } + + [Fact] + public void TestExtensionDeclaration_11() + { + // type parameter constraints + var compilation = Compile(""" + static class E + { + extension(int) where T : class + { + public void M() { } + } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + internal static class E : global::System.Object + { + extension(global::System.Int32) + where T : class + { + public void M() + { + } + } + } + """); + } + + [Fact] + public void Operator_01() + { + var compilation = Compile(""" + class C + { + public static C operator+(C c1, C c2) => throw null; + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("C").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + internal class C : global::System.Object + { + public static global::C operator +(global::C c1, global::C c2) + { + } + + public C() + { + } + } + """); + } + private static void TestModifiersAsync(DeclarationModifiers modifiers, string markup) { MarkupTestFile.GetSpan(markup, out var code, out var span); diff --git a/src/roslyn/src/Workspaces/Core/Desktop/Workspace/Host/Mef/MefV1HostServices.cs b/src/roslyn/src/Workspaces/Core/Desktop/Workspace/Host/Mef/MefV1HostServices.cs index e48e2eec2de..c098a0aa5c0 100644 --- a/src/roslyn/src/Workspaces/Core/Desktop/Workspace/Host/Mef/MefV1HostServices.cs +++ b/src/roslyn/src/Workspaces/Core/Desktop/Workspace/Host/Mef/MefV1HostServices.cs @@ -106,7 +106,7 @@ public IEnumerable> GetExports() } internal TestAccessor GetTestAccessor() - => new TestAccessor(this); + => new(this); private readonly struct ExportKey : IEquatable { diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/CodeFix.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/CodeFix.cs index f7b15e6fe3e..15a3d41632b 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/CodeFix.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/CodeFix.cs @@ -2,12 +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.Generic; using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.CodeFixes; @@ -18,60 +14,19 @@ namespace Microsoft.CodeAnalysis.CodeFixes; /// internal sealed class CodeFix { - public readonly Project Project; public readonly CodeAction Action; - public readonly ImmutableArray Diagnostics; /// - /// This is the diagnostic that will show up in the preview pane header when a particular fix is selected in the - /// light bulb menu. We also group all fixes with the same together (into a single - /// SuggestedActionSet) in the light bulb menu. + /// Note: a code fix can fix one or more diagnostics. For the purposes of display in a UI, it is recommended that + /// the first diagnostic in this list be treated as the "primary" diagnostic. For example, withing Visual Studio, + /// all fixes with the same primary diagnostic are grouped together in the light bulb menu. /// - /// - /// A given fix can fix one or more diagnostics. However, our light bulb UI (preview pane, grouping of fixes in the - /// light bulb menu etc.) currently keeps things simple and pretends that each fix fixes a single . - /// - /// Implementation-wise the is always the first diagnostic that the supplied when registering the fix (). This could change in the future, - /// if we decide to change the UI to depict the true mapping between fixes and diagnostics or if we decide to use - /// some other heuristic to determine the . - /// - public Diagnostic PrimaryDiagnostic => Diagnostics[0]; - - public CodeFix(Project project, CodeAction action, Diagnostic diagnostic) - { - Project = project; - Action = action; - Diagnostics = [diagnostic]; - } + public readonly ImmutableArray Diagnostics; - public CodeFix(Project project, CodeAction action, ImmutableArray diagnostics) + public CodeFix(CodeAction action, ImmutableArray diagnostics) { - Debug.Assert(!diagnostics.IsDefaultOrEmpty); - Project = project; + Contract.ThrowIfTrue(diagnostics.IsDefaultOrEmpty); Action = action; Diagnostics = diagnostics; } - - public DiagnosticData GetPrimaryDiagnosticData() - { - var diagnostic = PrimaryDiagnostic; - - if (diagnostic.Location.IsInSource) - { - var document = Project.GetDocument(diagnostic.Location.SourceTree); - if (document != null) - return DiagnosticData.Create(diagnostic, document); - } - else if (diagnostic.Location.Kind == LocationKind.ExternalFile) - { - var document = Project.Documents.FirstOrDefault(d => d.FilePath == diagnostic.Location.GetLineSpan().Path); - if (document != null) - return DiagnosticData.Create(diagnostic, document); - } - - return DiagnosticData.Create(diagnostic, Project); - } } diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/CodeFixProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/CodeFixProvider.cs index b5d436d2d64..ac09a56a53c 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/CodeFixProvider.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/CodeFixProvider.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; namespace Microsoft.CodeAnalysis.CodeFixes; @@ -13,7 +14,7 @@ namespace Microsoft.CodeAnalysis.CodeFixes; /// Implement this type to provide fixes for source code problems. /// Remember to use so the host environment can offer your fixes in a UI. /// -public abstract class CodeFixProvider +public abstract class CodeFixProvider : IRefactorOrFixProvider { private protected ImmutableArray CustomTags = []; diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DocumentBasedFixAllProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DocumentBasedFixAllProvider.cs index 796d9d7f1ce..2373f19e382 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DocumentBasedFixAllProvider.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DocumentBasedFixAllProvider.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; -using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContext.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContext.cs index a450cde44c7..a42d6ac9119 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContext.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContext.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.CodeFixes; /// /// Context for "Fix all occurrences" code fixes provided by a . /// -public partial class FixAllContext : IFixAllContext +public partial class FixAllContext : IRefactorOrFixAllContext { internal FixAllState State { get; } @@ -71,14 +71,14 @@ public partial class FixAllContext : IFixAllContext public IProgress Progress { get; } #region IFixAllContext implementation - IFixAllState IFixAllContext.State => this.State; + IRefactorOrFixAllState IRefactorOrFixAllContext.State => this.State; - object IFixAllContext.Provider => this.CodeFixProvider; + IRefactorOrFixProvider IRefactorOrFixAllContext.Provider => this.CodeFixProvider; - string IFixAllContext.GetDefaultFixAllTitle() + string IRefactorOrFixAllContext.GetDefaultTitle() => this.GetDefaultFixAllTitle(); - IFixAllContext IFixAllContext.With( + IRefactorOrFixAllContext IRefactorOrFixAllContext.With( Optional<(Document? document, Project project)> documentAndProject, Optional scope, Optional codeActionEquivalenceKey, diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllProvider.cs index b5aac3e0083..eb47a4f119f 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllProvider.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllProvider.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.CodeFixes; /// Implement this abstract type to provide fix all/multiple occurrences code fixes for source code problems. /// Alternatively, you can use any of the well known fix all providers from . /// -public abstract class FixAllProvider : IFixAllProvider +public abstract class FixAllProvider : IRefactorOrFixAllProvider { private protected static ImmutableArray DefaultSupportedFixAllScopes = [FixAllScope.Document, FixAllScope.Project, FixAllScope.Solution]; @@ -24,7 +24,7 @@ private protected static ImmutableArray DefaultSupportedFixAllScope public virtual IEnumerable GetSupportedFixAllScopes() => DefaultSupportedFixAllScopes; - CodeActionCleanup IFixAllProvider.Cleanup => this.Cleanup; + CodeActionCleanup IRefactorOrFixAllProvider.Cleanup => this.Cleanup; internal virtual CodeActionCleanup Cleanup => CodeActionCleanup.Default; @@ -96,7 +96,7 @@ internal static FixAllProvider Create( } #region IFixAllProvider implementation - Task IFixAllProvider.GetFixAsync(IFixAllContext fixAllContext) + Task IRefactorOrFixAllProvider.GetCodeActionAsync(IRefactorOrFixAllContext fixAllContext) => this.GetFixAsync((FixAllContext)fixAllContext); #endregion diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllScope.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllScope.cs index 7685dda9619..83c8a601567 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllScope.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllScope.cs @@ -10,22 +10,22 @@ namespace Microsoft.CodeAnalysis.CodeFixes; public enum FixAllScope { /// - /// Scope to fix all occurences of diagnostic(s) in the entire document. + /// Scope to fix all occurrences of diagnostic(s) in the entire document. /// Document, /// - /// Scope to fix all occurences of diagnostic(s) in the entire project. + /// Scope to fix all occurrences of diagnostic(s) in the entire project. /// Project, /// - /// Scope to fix all occurences of diagnostic(s) in the entire solution. + /// Scope to fix all occurrences of diagnostic(s) in the entire solution. /// Solution, /// - /// Custom scope to fix all occurences of diagnostic(s). This scope can + /// Custom scope to fix all occurrences of diagnostic(s). This scope can /// be used by custom s and custom code fix engines. /// Custom, diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/CommonFixAllState.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/CommonFixAllState.cs index 8bc5930cccc..715fcb805a0 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/CommonFixAllState.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/CommonFixAllState.cs @@ -3,13 +3,15 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Internal.Log; -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; -internal abstract partial class CommonFixAllState : IFixAllState - where TFixAllProvider : IFixAllProvider +internal abstract partial class CommonFixAllState + : IRefactorOrFixAllState + where TProvider : IRefactorOrFixProvider + where TFixAllProvider : IRefactorOrFixAllProvider where TFixAllState : CommonFixAllState { public int CorrelationId { get; } = CorrelationIdFactory.GetNextId(); @@ -63,11 +65,11 @@ public TFixAllState With( } #region IFixAllState implementation - IFixAllProvider IFixAllState.FixAllProvider => this.FixAllProvider!; + IRefactorOrFixAllProvider IRefactorOrFixAllState.FixAllProvider => this.FixAllProvider; - object IFixAllState.Provider => this.Provider!; + IRefactorOrFixProvider IRefactorOrFixAllState.Provider => this.Provider; - IFixAllState IFixAllState.With( + IRefactorOrFixAllState IRefactorOrFixAllState.With( Optional<(Document? document, Project project)> documentAndProject, Optional scope, Optional codeActionEquivalenceKey) diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DefaultFixAllProviderHelpers.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DefaultFixAllProviderHelpers.cs index 9e2cd45ee9c..bd3b8999969 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DefaultFixAllProviderHelpers.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DefaultFixAllProviderHelpers.cs @@ -24,7 +24,7 @@ internal static class DefaultFixAllProviderHelpers string title, TFixAllContext fixAllContext, Func, Task> fixAllContextsAsync) - where TFixAllContext : IFixAllContext + where TFixAllContext : IRefactorOrFixAllContext { // We're about to do a lot of computation to compute all the diagnostics needed and to perform the // changes. Keep this solution alive on the OOP side so that we never drop it and then resync it @@ -50,19 +50,19 @@ FixAllScope.Document or FixAllScope.ContainingMember or FixAllScope.ContainingTy private static Task GetDocumentFixesAsync( TFixAllContext fixAllContext, Func, Task> fixAllContextsAsync) - where TFixAllContext : IFixAllContext + where TFixAllContext : IRefactorOrFixAllContext => fixAllContextsAsync(fixAllContext, [fixAllContext]); private static Task GetProjectFixesAsync( TFixAllContext fixAllContext, Func, Task> fixAllContextsAsync) - where TFixAllContext : IFixAllContext + where TFixAllContext : IRefactorOrFixAllContext => fixAllContextsAsync(fixAllContext, [(TFixAllContext)fixAllContext.With((document: null, fixAllContext.State.Project))]); private static Task GetSolutionFixesAsync( TFixAllContext fixAllContext, Func, Task> fixAllContextsAsync) - where TFixAllContext : IFixAllContext + where TFixAllContext : IRefactorOrFixAllContext { var solution = fixAllContext.State.Solution; var dependencyGraph = solution.GetProjectDependencyGraph(); diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DocumentBasedFixAllProviderHelpers.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DocumentBasedFixAllProviderHelpers.cs index dbeca1f4975..a159f7a0838 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DocumentBasedFixAllProviderHelpers.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DocumentBasedFixAllProviderHelpers.cs @@ -26,7 +26,7 @@ internal static class DocumentBasedFixAllProviderHelpers IProgress progressTracker, string progressTrackerDescription, Func, Task> getFixedDocumentsAsync) - where TFixAllContext : IFixAllContext + where TFixAllContext : IRefactorOrFixAllContext { var cancellationToken = originalFixAllContext.CancellationToken; diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllKind.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllKind.cs index b0f45df201e..2c30c8b9329 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllKind.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllKind.cs @@ -5,7 +5,7 @@ namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; /// -/// An enum to distinguish if we are performing a Fix all occurences for a code fix or a code refactoring. +/// An enum to distinguish if we are performing a Fix all occurrences for a code fix or a code refactoring. /// internal enum FixAllKind { diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllLogger.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllLogger.cs index 3cb0a3e384c..9a2460f00fc 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllLogger.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllLogger.cs @@ -38,7 +38,7 @@ internal static class FixAllLogger private const string TotalDiagnosticsToFix = nameof(TotalDiagnosticsToFix); private const string TotalFixesToMerge = nameof(TotalFixesToMerge); - public static void LogState(IFixAllState fixAllState, bool isInternalProvider) + public static void LogState(IRefactorOrFixAllState fixAllState, bool isInternalProvider) { FunctionId functionId; string providerKey; diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllProviderInfo.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllProviderInfo.cs index 0029db84f0f..66bdccd8d01 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllProviderInfo.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllProviderInfo.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeRefactorings; @@ -16,11 +15,11 @@ namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; /// internal abstract class FixAllProviderInfo { - public readonly IFixAllProvider FixAllProvider; + public readonly IRefactorOrFixAllProvider FixAllProvider; public readonly ImmutableArray SupportedScopes; private FixAllProviderInfo( - IFixAllProvider fixAllProvider, + IRefactorOrFixAllProvider fixAllProvider, ImmutableArray supportedScopes) { FixAllProvider = fixAllProvider; @@ -69,19 +68,15 @@ private FixAllProviderInfo( /// private static FixAllProviderInfo? CreateWithCodeRefactoring(CodeRefactoringProvider provider) { - var fixAllProvider = provider.GetFixAllProvider(); - if (fixAllProvider == null) - { + var refactorAllProvider = provider.GetRefactorAllProvider(); + if (refactorAllProvider == null) return null; - } - var scopes = fixAllProvider.GetSupportedFixAllScopes().ToImmutableArrayOrEmpty(); + var scopes = refactorAllProvider.GetSupportedRefactorAllScopes().ToImmutableArrayOrEmpty(); if (scopes.IsEmpty) - { return null; - } - return new CodeRefactoringFixAllProviderInfo(fixAllProvider, scopes); + return new CodeRefactoringFixAllProviderInfo(refactorAllProvider, scopes); } /// @@ -91,15 +86,11 @@ private FixAllProviderInfo( { var fixAllProvider = provider.GetFixAllProvider(); if (fixAllProvider == null) - { return null; - } var scopes = fixAllProvider.GetSupportedFixAllScopes().ToImmutableArrayOrEmpty(); if (scopes.IsEmpty) - { return null; - } return new SuppressionFixerFixAllProviderInfo(fixAllProvider, provider, scopes); } @@ -107,7 +98,7 @@ private FixAllProviderInfo( public abstract bool CanBeFixed(Diagnostic diagnostic); private sealed class CodeFixerFixAllProviderInfo( - IFixAllProvider fixAllProvider, + IRefactorOrFixAllProvider fixAllProvider, ImmutableArray supportedDiagnosticIds, ImmutableArray supportedScopes) : FixAllProviderInfo(fixAllProvider, supportedScopes) { @@ -116,9 +107,10 @@ public override bool CanBeFixed(Diagnostic diagnostic) } private sealed class SuppressionFixerFixAllProviderInfo( - IFixAllProvider fixAllProvider, + IRefactorOrFixAllProvider fixAllProvider, IConfigurationFixProvider suppressionFixer, - ImmutableArray supportedScopes) : FixAllProviderInfo(fixAllProvider, supportedScopes) + ImmutableArray supportedScopes) + : FixAllProviderInfo(fixAllProvider, supportedScopes) { private readonly Func _canBeSuppressedOrUnsuppressed = suppressionFixer.IsFixableDiagnostic; @@ -127,8 +119,9 @@ public override bool CanBeFixed(Diagnostic diagnostic) } private sealed class CodeRefactoringFixAllProviderInfo( - IFixAllProvider fixAllProvider, - ImmutableArray supportedScopes) : FixAllProviderInfo(fixAllProvider, supportedScopes) + IRefactorOrFixAllProvider fixAllProvider, + ImmutableArray supportedScopes) + : FixAllProviderInfo(fixAllProvider, supportedScopes.SelectAsArray(s => s.ToFixAllScope())) { public override bool CanBeFixed(Diagnostic diagnostic) => throw ExceptionUtilities.Unreachable(); diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllContext.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixAllContext.cs similarity index 69% rename from src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllContext.cs rename to src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixAllContext.cs index e4a930af403..a14de03aff8 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllContext.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixAllContext.cs @@ -5,21 +5,22 @@ using System; using System.Threading; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeRefactorings; namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; /// -/// Represents a FixAllContext for code fixes or refactorings. +/// Represents a or . /// -internal interface IFixAllContext +internal interface IRefactorOrFixAllContext { - IFixAllState State { get; } - object Provider { get; } + IRefactorOrFixAllState State { get; } + IRefactorOrFixProvider Provider { get; } CancellationToken CancellationToken { get; } IProgress Progress { get; } - string GetDefaultFixAllTitle(); - IFixAllContext With( + string GetDefaultTitle(); + IRefactorOrFixAllContext With( Optional<(Document? document, Project project)> documentAndProject = default, Optional scope = default, Optional codeActionEquivalenceKey = default, diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixAllProvider.cs similarity index 55% rename from src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllProvider.cs rename to src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixAllProvider.cs index 50db400fe36..b540c56f5d9 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllProvider.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixAllProvider.cs @@ -6,24 +6,16 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeRefactorings; namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; /// -/// Represents a FixAllProvider for code fixes or refactorings. +/// Allows for generic handling of a or . /// -internal interface IFixAllProvider +internal interface IRefactorOrFixAllProvider { - /// - /// Gets the supported scopes for applying multiple occurrences of a code refactoring. - /// By default, it returns the following scopes: - /// (a) - /// (b) and - /// (c) - /// - IEnumerable GetSupportedFixAllScopes(); - - Task GetFixAsync(IFixAllContext fixAllContext); + Task GetCodeActionAsync(IRefactorOrFixAllContext fixAllContext); /// /// The sort of cleanup that should automatically be poerformed for this fix all provider. By default this is diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllState.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixAllState.cs similarity index 71% rename from src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllState.cs rename to src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixAllState.cs index e714ebe17dc..561d10037db 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllState.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixAllState.cs @@ -2,17 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeRefactorings; namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; /// -/// Represents internal FixAllState for code fixes or refactorings. +/// Represents internal or . /// -internal interface IFixAllState +internal interface IRefactorOrFixAllState { int CorrelationId { get; } - IFixAllProvider FixAllProvider { get; } + IRefactorOrFixAllProvider FixAllProvider { get; } string? CodeActionEquivalenceKey { get; } FixAllScope Scope { get; } FixAllKind FixAllKind { get; } @@ -23,9 +24,9 @@ internal interface IFixAllState /// /// Underlying code fix provider or code refactoring provider for the fix all occurrences fix. /// - object Provider { get; } + IRefactorOrFixProvider Provider { get; } - IFixAllState With( + IRefactorOrFixAllState With( Optional<(Document? document, Project project)> documentAndProject = default, Optional scope = default, Optional codeActionEquivalenceKey = default); diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixProvider.cs new file mode 100644 index 00000000000..452f5a6da5b --- /dev/null +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixProvider.cs @@ -0,0 +1,9 @@ +// 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.CodeFixesAndRefactorings; + +internal interface IRefactorOrFixProvider +{ +} diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs index 9985293a1b3..d28dd203ede 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; namespace Microsoft.CodeAnalysis.CodeRefactorings; @@ -13,7 +14,7 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings; /// Inherit this type to provide source code refactorings. /// Remember to use so the host environment can offer your refactorings in a UI. /// -public abstract class CodeRefactoringProvider +public abstract class CodeRefactoringProvider : IRefactorOrFixProvider { private protected ImmutableArray CustomTags = []; @@ -23,11 +24,11 @@ public abstract class CodeRefactoringProvider public abstract Task ComputeRefactoringsAsync(CodeRefactoringContext context); /// - /// Gets an optional that can apply multiple occurrences of code refactoring(s) - /// registered by this code refactoring provider across the supported s. - /// Return null if the provider doesn't support fix all operation. + /// Gets an optional that can apply multiple occurrences of code refactoring(s) + /// registered by this code refactoring provider across the supported s. + /// Return null if the provider doesn't support the refactor all operation. /// - internal virtual FixAllProvider? GetFixAllProvider() + public virtual RefactorAllProvider? GetRefactorAllProvider() => null; /// diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs deleted file mode 100644 index 1617b9c9453..00000000000 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs +++ /dev/null @@ -1,102 +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.Immutable; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; -using Microsoft.CodeAnalysis.Shared.Utilities; -using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; - -namespace Microsoft.CodeAnalysis.CodeRefactorings; - -/// -/// Provides a base class to write a for refactorings that fixes documents independently. -/// This type should be used in the case where the code refactoring(s) only affect individual s. -/// -/// -/// This type provides suitable logic for fixing large solutions in an efficient manner. Projects are serially -/// processed, with all the documents in the project being processed in parallel. -/// is invoked for each document for implementors to process. -/// -/// TODO: Make public, tracked with https://github.com/dotnet/roslyn/issues/60703 -/// -internal abstract class DocumentBasedFixAllProvider(ImmutableArray supportedFixAllScopes) : FixAllProvider -{ - private readonly ImmutableArray _supportedFixAllScopes = supportedFixAllScopes; - - protected DocumentBasedFixAllProvider() - : this(DefaultSupportedFixAllScopes) - { - } - - /// - /// Produce a suitable title for the fix-all this type creates in . Override this if customizing that title is desired. - /// - protected virtual string GetFixAllTitle(FixAllContext fixAllContext) - => fixAllContext.GetDefaultFixAllTitle(); - - /// - /// Apply fix all operation for the code refactoring in the - /// for the given . The document returned will only be examined for its content - /// (e.g. it's or . No other aspects of document (like it's properties), - /// or changes to the or it points at will be considered. - /// - /// The context for the Fix All operation. - /// The document to fix. - /// The spans to fix in the document. If not specified, entire document needs to be fixedd. - /// - /// The new representing the content fixed document. - /// -or- - /// , if no changes were made to the document. - /// - protected abstract Task FixAllAsync(FixAllContext fixAllContext, Document document, Optional> fixAllSpans); - - public sealed override IEnumerable GetSupportedFixAllScopes() - => _supportedFixAllScopes; - - public sealed override Task GetFixAsync(FixAllContext fixAllContext) - => DefaultFixAllProviderHelpers.GetFixAsync( - fixAllContext.GetDefaultFixAllTitle(), fixAllContext, FixAllContextsHelperAsync); - - private Task FixAllContextsHelperAsync(FixAllContext originalFixAllContext, ImmutableArray fixAllContexts) - => DocumentBasedFixAllProviderHelpers.FixAllContextsAsync( - originalFixAllContext, - fixAllContexts, - originalFixAllContext.Progress, - this.GetFixAllTitle(originalFixAllContext), - GetFixedDocumentsAsync); - - /// - /// Attempts to apply fix all operations returning, for each updated document, either the new syntax root for that - /// document or its new text. Syntax roots are returned for documents that support them, and are used to perform a - /// final cleanup pass for formatting/simplification/etc. Text is returned for documents that don't support syntax. - /// - private async Task GetFixedDocumentsAsync( - FixAllContext fixAllContext, Func onDocumentFixed) - { - Contract.ThrowIfFalse(fixAllContext.Scope is FixAllScope.Document or FixAllScope.Project - or FixAllScope.ContainingMember or FixAllScope.ContainingType); - - var cancellationToken = fixAllContext.CancellationToken; - - // Process all documents in parallel to get the change for each doc. - var documentsAndSpansToFix = await fixAllContext.GetFixAllSpansAsync(cancellationToken).ConfigureAwait(false); - - await Parallel.ForEachAsync( - source: documentsAndSpansToFix, - cancellationToken, - async (tuple, cancellationToken) => - { - var (document, spans) = tuple; - var newDocument = await this.FixAllAsync(fixAllContext, document, spans).ConfigureAwait(false); - await onDocumentFixed(document, newDocument).ConfigureAwait(false); - }).ConfigureAwait(false); - } -} diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedRefactorAllProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedRefactorAllProvider.cs new file mode 100644 index 00000000000..44e399d303a --- /dev/null +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedRefactorAllProvider.cs @@ -0,0 +1,104 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CodeRefactorings; + +/// +/// Provides a base class to write a for refactorings that refactor documents +/// independently. This type should be used in the case where the code refactoring(s) only affect individual s. +/// +/// +/// This type provides suitable logic for refactorings large solutions in an efficient manner. Projects are serially +/// processed, with all the documents in the project being processed in parallel. is +/// invoked for each document for implementors to process. +/// +/// TODO: Make public, tracked with https://github.com/dotnet/roslyn/issues/60703 +/// +internal abstract class DocumentBasedRefactorAllProvider(ImmutableArray supportedRefactorAllScopes) + : RefactorAllProvider +{ + private readonly ImmutableArray _supportedRefactorAllScopes = supportedRefactorAllScopes; + + protected DocumentBasedRefactorAllProvider() + : this(DefaultSupportedRefactorAllScopes) + { + } + + /// + /// Produce a suitable title for the refactor-all this type creates in . Override this if customizing that title is desired. + /// + protected virtual string GetRefactorAllTitle(RefactorAllContext refactorAllContext) + => refactorAllContext.GetDefaultRefactorAllTitle(); + + /// + /// Apply refactor all operation for the code refactoring in the for the + /// given . The document returned will only be examined for its content (e.g. + /// it's or . No other aspects of document (like it's properties), + /// or changes to the or it points at will be considered. + /// + /// The context for the Refactor All operation. + /// The document to refactor. + /// The spans to refactor in the document. If not specified, entire document needs to be refactored. + /// + /// The new representing the content refactored document. + /// -or- + /// , if no changes were made to the document. + /// + protected abstract Task RefactorAllAsync( + RefactorAllContext refactorAllContext, Document document, Optional> refactorAllSpans); + + public sealed override IEnumerable GetSupportedRefactorAllScopes() + => _supportedRefactorAllScopes; + + public sealed override Task GetRefactoringAsync(RefactorAllContext refactorAllContext) + => DefaultFixAllProviderHelpers.GetFixAsync( + refactorAllContext.GetDefaultRefactorAllTitle(), refactorAllContext, RefactorAllContextsHelperAsync); + + private Task RefactorAllContextsHelperAsync(RefactorAllContext originalRefactorAllContext, ImmutableArray refactorAllContexts) + => DocumentBasedFixAllProviderHelpers.FixAllContextsAsync( + originalRefactorAllContext, + refactorAllContexts, + originalRefactorAllContext.Progress, + this.GetRefactorAllTitle(originalRefactorAllContext), + GetRefactoredDocumentsAsync); + + /// + /// Attempts to apply refactor all operations. Returning, for each updated document, either the new syntax root for + /// that document or its new text. Syntax roots are returned for documents that support them, and are used to + /// perform a final cleanup pass for formatting/simplification/etc. Text is returned for documents that don't + /// support syntax. + /// + private async Task GetRefactoredDocumentsAsync( + RefactorAllContext refactorAllContext, Func onDocumentRefactored) + { + Contract.ThrowIfFalse(refactorAllContext.Scope is RefactorAllScope.Document or RefactorAllScope.Project + or RefactorAllScope.ContainingMember or RefactorAllScope.ContainingType); + + var cancellationToken = refactorAllContext.CancellationToken; + + // Process all documents in parallel to get the change for each doc. + var documentsAndSpansToRefactor = await refactorAllContext.GetRefactorAllSpansAsync(cancellationToken).ConfigureAwait(false); + + await Parallel.ForEachAsync( + source: documentsAndSpansToRefactor, + cancellationToken, + async (tuple, cancellationToken) => + { + var (document, spans) = tuple; + var newDocument = await this.RefactorAllAsync(refactorAllContext, document, spans).ConfigureAwait(false); + await onDocumentRefactored(document, newDocument).ConfigureAwait(false); + }).ConfigureAwait(false); + } +} diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs deleted file mode 100644 index 4bb45d22e18..00000000000 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs +++ /dev/null @@ -1,106 +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.Immutable; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; -using Microsoft.CodeAnalysis.Text; -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; - -namespace Microsoft.CodeAnalysis.CodeRefactorings; - -/// -/// Implement this abstract type to provide fix all occurrences support for code refactorings. -/// -/// -/// TODO: Make public, tracked with https://github.com/dotnet/roslyn/issues/60703 -/// -internal abstract class FixAllProvider : IFixAllProvider -{ - private protected static ImmutableArray DefaultSupportedFixAllScopes - = [FixAllScope.Document, FixAllScope.Project, FixAllScope.Solution]; - - public virtual IEnumerable GetSupportedFixAllScopes() - => DefaultSupportedFixAllScopes; - - public virtual CodeActionCleanup Cleanup => CodeActionCleanup.Default; - - /// - /// Gets fix all occurrences fix for the given fixAllContext. - /// - public abstract Task GetFixAsync(FixAllContext fixAllContext); - - #region IFixAllProvider implementation - Task IFixAllProvider.GetFixAsync(IFixAllContext fixAllContext) - => this.GetFixAsync((FixAllContext)fixAllContext); - #endregion - - /// - /// Create a that fixes documents independently. - /// This can be used in the case where refactoring(s) registered by this provider - /// only affect a single . - /// - /// - /// Callback that will apply the refactorings present in the provided document. The document returned will only be - /// examined for its content (e.g. it's or . No other aspects - /// of it (like attributes), or changes to the or it points at - /// will be considered. - /// - public static FixAllProvider Create(Func>, Task> fixAllAsync) - => Create(fixAllAsync, DefaultSupportedFixAllScopes); - - /// - /// Create a that fixes documents independently. - /// This can be used in the case where refactoring(s) registered by this provider - /// only affect a single . - /// - /// - /// Callback that will apply the refactorings present in the provided document. The document returned will only be - /// examined for its content (e.g. it's or . No other aspects - /// of it (like attributes), or changes to the or it points at - /// will be considered. - /// - /// - /// Supported s for the fix all provider. - /// Note that is not supported by the - /// and should not be part of the supported scopes. - /// - public static FixAllProvider Create( - Func>, Task> fixAllAsync, - ImmutableArray supportedFixAllScopes) - { - return Create(fixAllAsync, supportedFixAllScopes, CodeActionCleanup.Default); - } - - internal static FixAllProvider Create( - Func>, Task> fixAllAsync, - ImmutableArray supportedFixAllScopes, - CodeActionCleanup cleanup) - { - if (fixAllAsync is null) - throw new ArgumentNullException(nameof(fixAllAsync)); - - if (supportedFixAllScopes.IsDefault) - throw new ArgumentNullException(nameof(supportedFixAllScopes)); - - if (supportedFixAllScopes.Contains(FixAllScope.Custom)) - throw new ArgumentException(WorkspacesResources.FixAllScope_Custom_is_not_supported_with_this_API, nameof(supportedFixAllScopes)); - - return new CallbackDocumentBasedFixAllProvider(fixAllAsync, supportedFixAllScopes, cleanup); - } - - private sealed class CallbackDocumentBasedFixAllProvider( - Func>, Task> fixAllAsync, - ImmutableArray supportedFixAllScopes, - CodeActionCleanup cleanup) : DocumentBasedFixAllProvider(supportedFixAllScopes) - { - public override CodeActionCleanup Cleanup { get; } = cleanup; - - protected override Task FixAllAsync(FixAllContext context, Document document, Optional> fixAllSpans) - => fixAllAsync(context, document, fixAllSpans); - } -} diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllContext.cs similarity index 50% rename from src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs rename to src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllContext.cs index 7dec1f30e25..dfa4a28d82b 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllContext.cs @@ -7,78 +7,83 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.Text; -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; namespace Microsoft.CodeAnalysis.CodeRefactorings; /// -/// Context for "Fix all occurrences" for code refactorings provided by each . +/// Context for "Refactor all occurrences" for code refactorings provided by each . /// /// /// TODO: Make public, tracked with https://github.com/dotnet/roslyn/issues/60703 /// -internal sealed class FixAllContext : IFixAllContext +public sealed class RefactorAllContext : IRefactorOrFixAllContext { - internal FixAllState State { get; } + internal RefactorAllState State { get; } - internal FixAllProvider FixAllProvider => State.FixAllProvider; + internal RefactorAllProvider RefactorAllProvider => State.FixAllProvider; /// - /// Document within which fix all occurrences was triggered. + /// Document within which refactor all occurrences was triggered. /// public Document Document => State.Document!; /// - /// Underlying which triggered this fix all. + /// Underlying which triggered this refactor all. /// public CodeRefactoringProvider CodeRefactoringProvider => State.Provider; /// - /// to fix all occurrences. + /// to refactor all occurrences. /// - public FixAllScope Scope => State.Scope; + public RefactorAllScope Scope => (RefactorAllScope)State.Scope; /// - /// The value expected of a participating in this fix all. + /// The value expected of a participating in this + /// refactor all. /// public string? CodeActionEquivalenceKey => State.CodeActionEquivalenceKey; /// - /// CancellationToken for fix all session. + /// CancellationToken for refactor all session. /// public CancellationToken CancellationToken { get; } public IProgress Progress { get; } /// - /// Project to fix all occurrences. - /// Note that this property will always be the containing project of - /// for publicly exposed FixAllContext instance. However, we might create an intermediate FixAllContext - /// with null and non-null Project, so we require this internal property for intermediate computation. + /// Project to refactor all occurrences within. /// public Project Project => State.Project; public Solution Solution => Project.Solution; #region IFixAllContext implementation - IFixAllState IFixAllContext.State => this.State; + IRefactorOrFixAllState IRefactorOrFixAllContext.State => this.State; - object IFixAllContext.Provider => this.CodeRefactoringProvider; + IRefactorOrFixProvider IRefactorOrFixAllContext.Provider => this.CodeRefactoringProvider; - string IFixAllContext.GetDefaultFixAllTitle() => this.GetDefaultFixAllTitle(); + string IRefactorOrFixAllContext.GetDefaultTitle() => this.GetDefaultRefactorAllTitle(); - IFixAllContext IFixAllContext.With( + IRefactorOrFixAllContext IRefactorOrFixAllContext.With( Optional<(Document? document, Project project)> documentAndProject, Optional scope, Optional codeActionEquivalenceKey, Optional cancellationToken) - => this.With(documentAndProject, scope, codeActionEquivalenceKey, cancellationToken); + { + var newState = State.With(documentAndProject, scope, codeActionEquivalenceKey); + var newCancellationToken = cancellationToken.HasValue ? cancellationToken.Value : this.CancellationToken; + + return State == newState && CancellationToken == newCancellationToken + ? this + : new RefactorAllContext(newState, this.Progress, newCancellationToken); + } #endregion - internal FixAllContext( - FixAllState state, + internal RefactorAllContext( + RefactorAllState state, IProgress progressTracker, CancellationToken cancellationToken) { @@ -88,26 +93,12 @@ internal FixAllContext( } /// - /// Gets the spans to fix by document for the for this fix all occurences fix. - /// If no spans are specified, it indicates the entire document needs to be fixed. + /// Gets the spans to refactor by document for the for this refactor all occurrences fix. If no + /// spans are specified, it indicates the entire document needs to be refactored. /// - public Task>>> GetFixAllSpansAsync(CancellationToken cancellationToken) - => State.GetFixAllSpansAsync(cancellationToken); - - internal FixAllContext With( - Optional<(Document? document, Project project)> documentAndProject = default, - Optional scope = default, - Optional codeActionEquivalenceKey = default, - Optional cancellationToken = default) - { - var newState = State.With(documentAndProject, scope, codeActionEquivalenceKey); - var newCancellationToken = cancellationToken.HasValue ? cancellationToken.Value : this.CancellationToken; - - return State == newState && CancellationToken == newCancellationToken - ? this - : new FixAllContext(newState, this.Progress, newCancellationToken); - } + public Task>>> GetRefactorAllSpansAsync(CancellationToken cancellationToken) + => State.GetRefactorAllSpansAsync(cancellationToken); - internal string GetDefaultFixAllTitle() - => FixAllHelper.GetDefaultFixAllTitle(this.Scope, this.State.CodeActionTitle, this.Document, this.Project); + internal string GetDefaultRefactorAllTitle() + => FixAllHelper.GetDefaultFixAllTitle(this.Scope.ToFixAllScope(), this.State.CodeActionTitle, this.Document, this.Project); } diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllProvider.cs new file mode 100644 index 00000000000..1b1d6d29609 --- /dev/null +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllProvider.cs @@ -0,0 +1,107 @@ +// 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.Linq; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.CodeRefactorings; + +/// +/// Implement this abstract type to provide refactor all occurrences support for code refactorings. +/// +/// +/// TODO: Make public, tracked with https://github.com/dotnet/roslyn/issues/60703 +/// +public abstract class RefactorAllProvider : IRefactorOrFixAllProvider +{ + private protected static ImmutableArray DefaultSupportedRefactorAllScopes + = [RefactorAllScope.Document, RefactorAllScope.Project, RefactorAllScope.Solution]; + + public virtual IEnumerable GetSupportedRefactorAllScopes() + => DefaultSupportedRefactorAllScopes; + + internal virtual CodeActionCleanup Cleanup => CodeActionCleanup.Default; + + CodeActionCleanup IRefactorOrFixAllProvider.Cleanup => this.Cleanup; + + /// + /// Gets refactor all occurrences for the given . + /// + public abstract Task GetRefactoringAsync(RefactorAllContext refactorAllContext); + + #region IFixAllProvider implementation + Task IRefactorOrFixAllProvider.GetCodeActionAsync(IRefactorOrFixAllContext fixAllContext) + => this.GetRefactoringAsync((RefactorAllContext)fixAllContext); + #endregion + + /// + /// Create a that refactors documents independently. This can be used in the case + /// where refactoring(s) registered by this provider only affect a single . + /// + /// + /// Callback that will apply the refactorings present in the provided document. The document returned will only be + /// examined for its content (e.g. it's or . No other aspects + /// of it (like attributes), or changes to the or it points at + /// will be considered. + /// + public static RefactorAllProvider Create(Func>, Task> refactorAllAsync) + => Create(refactorAllAsync, DefaultSupportedRefactorAllScopes); + + /// + /// Create a that refactors documents independently. This can be used in the case + /// where refactoring(s) registered by this provider only affect a single . + /// + /// + /// Callback that will apply the refactorings present in the provided document. The document returned will only be + /// examined for its content (e.g. it's or . No other aspects + /// of it (like attributes), or changes to the or it points at + /// will be considered. + /// + /// + /// Supported s for the refactor all provider. + /// Note that is not supported by the + /// and should not be part of the supported scopes. + /// + public static RefactorAllProvider Create( + Func>, Task> refactorAllAsync, + ImmutableArray supportedRefactorAllScopes) + { + return Create(refactorAllAsync, supportedRefactorAllScopes, CodeActionCleanup.Default); + } + + internal static RefactorAllProvider Create( + Func>, Task> refactorAllAsync, + ImmutableArray supportedRefactorAllScopes, + CodeActionCleanup cleanup) + { + if (refactorAllAsync is null) + throw new ArgumentNullException(nameof(refactorAllAsync)); + + if (supportedRefactorAllScopes.IsDefault) + throw new ArgumentNullException(nameof(supportedRefactorAllScopes)); + + if (supportedRefactorAllScopes.Contains(RefactorAllScope.Custom)) + throw new ArgumentException(WorkspacesResources.FixAllScope_Custom_is_not_supported_with_this_API, nameof(supportedRefactorAllScopes)); + + return new CallbackDocumentBasedRefactorAllProvider(refactorAllAsync, supportedRefactorAllScopes, cleanup); + } + + private sealed class CallbackDocumentBasedRefactorAllProvider( + Func>, Task> refactorAllAsync, + ImmutableArray supportedRefactorAllScopes, + CodeActionCleanup cleanup) : DocumentBasedRefactorAllProvider(supportedRefactorAllScopes) + { + internal override CodeActionCleanup Cleanup { get; } = cleanup; + + protected override Task RefactorAllAsync(RefactorAllContext context, Document document, Optional> refactorAllSpans) + => refactorAllAsync(context, document, refactorAllSpans); + } +} diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllScope.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllScope.cs new file mode 100644 index 00000000000..1e9d463cf91 --- /dev/null +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllScope.cs @@ -0,0 +1,92 @@ +// 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 Microsoft.CodeAnalysis.CodeFixes; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CodeRefactorings; + +/// +/// Indicates scope for "Refactor all occurrences" code fixes provided by each . +/// +public enum RefactorAllScope +{ + /// + /// Scope to refactor all occurrences of diagnostic(s) in the entire document. + /// + Document, + + /// + /// Scope to refactor all occurrences of diagnostic(s) in the entire project. + /// + Project, + + /// + /// Scope to refactor all occurrences of diagnostic(s) in the entire solution. + /// + Solution, + + /// + /// Custom scope to refactor all occurrences of diagnostic(s). This scope can be used by custom s and custom code refactoring engines. + /// + Custom, + + /// + /// Scope to refactor all occurrences of diagnostic(s) in the containing member relative to the trigger span for the + /// original code refactoring. + /// + ContainingMember, + + /// + /// Scope to refactor all occurrences of diagnostic(s) in the containing type relative to the trigger span for the + /// original code refactoring. + /// + ContainingType, +} + +internal static class RefactorAllScopeExtensions +{ + static RefactorAllScopeExtensions() + { +#if DEBUG + // Ensures that RefactorAllScope and FixAllScope have the same set of values. + + var refactorFields = typeof(RefactorAllScope) + .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static) + .Select(f => (f.Name, Value: (int)f.GetValue(null)!)); + + var fixAllFields = typeof(FixAllScope) + .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static) + .Select(f => (f.Name, Value: (int)f.GetValue(null)!)); + + Contract.ThrowIfFalse(refactorFields.SetEquals(fixAllFields)); +#endif + } + + public static FixAllScope ToFixAllScope(this RefactorAllScope scope) + => scope switch + { + RefactorAllScope.Document => FixAllScope.Document, + RefactorAllScope.Project => FixAllScope.Project, + RefactorAllScope.Solution => FixAllScope.Solution, + RefactorAllScope.Custom => FixAllScope.Custom, + RefactorAllScope.ContainingMember => FixAllScope.ContainingMember, + RefactorAllScope.ContainingType => FixAllScope.ContainingType, + _ => throw ExceptionUtilities.Unreachable(), + }; + + public static RefactorAllScope ToRefactorAllScope(this FixAllScope scope) + => scope switch + { + FixAllScope.Document => RefactorAllScope.Document, + FixAllScope.Project => RefactorAllScope.Project, + FixAllScope.Solution => RefactorAllScope.Solution, + FixAllScope.Custom => RefactorAllScope.Custom, + FixAllScope.ContainingMember => RefactorAllScope.ContainingMember, + FixAllScope.ContainingType => RefactorAllScope.ContainingType, + _ => throw ExceptionUtilities.Unreachable(), + }; +} diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllState.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllState.cs similarity index 51% rename from src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllState.cs rename to src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllState.cs index d0e11df8f59..e80efd2df67 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllState.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllState.cs @@ -9,21 +9,20 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; namespace Microsoft.CodeAnalysis.CodeRefactorings; -internal sealed class FixAllState : CommonFixAllState +internal sealed class RefactorAllState : CommonFixAllState { /// - /// Original selection span from which FixAll was invoked. - /// This is used in - /// to compute fix all spans for - /// and scopes. + /// Original selection span from which refactor-all was invoked. This is used in to compute refactor all spans for and scopes. /// private readonly TextSpan _selectionSpan; @@ -31,68 +30,68 @@ internal sealed class FixAllState : CommonFixAllState - /// Gets the spans to fix by document for the for this fix all occurences fix. - /// If no spans are specified, it indicates the entire document needs to be fixed. + /// Gets the spans to refactor by document for the for this refactor all occurrences + /// action. If no spans are specified, it indicates the entire document needs to be refactored. /// - internal async Task>>> GetFixAllSpansAsync(CancellationToken cancellationToken) + internal async Task>>> GetRefactorAllSpansAsync(CancellationToken cancellationToken) { - IEnumerable? documentsToFix = null; - switch (this.Scope) + IEnumerable? documentsToRefactor = null; + switch (this.Scope.ToRefactorAllScope()) { - case FixAllScope.ContainingType or FixAllScope.ContainingMember: + case RefactorAllScope.ContainingType or RefactorAllScope.ContainingMember: Contract.ThrowIfNull(Document); var spanMappingService = Document.GetLanguageService(); if (spanMappingService is null) @@ -103,23 +102,23 @@ internal async Task KeyValuePair.Create(kvp.Key, new Optional>(kvp.Value))) .ToImmutableDictionaryOrEmpty(); - case FixAllScope.Document: + case RefactorAllScope.Document: Contract.ThrowIfNull(Document); - documentsToFix = [Document]; + documentsToRefactor = [Document]; break; - case FixAllScope.Project: - documentsToFix = Project.Documents; + case RefactorAllScope.Project: + documentsToRefactor = Project.Documents; break; - case FixAllScope.Solution: - documentsToFix = Project.Solution.Projects.SelectMany(p => p.Documents); + case RefactorAllScope.Solution: + documentsToRefactor = Project.Solution.Projects.SelectMany(p => p.Documents); break; default: return ImmutableDictionary>>.Empty; } - return documentsToFix.ToImmutableDictionary(d => d, _ => default(Optional>)); + return documentsToRefactor.ToImmutableDictionary(d => d, _ => default(Optional>)); } } diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs index 0ef5c4888c6..dc567787088 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs @@ -10,59 +10,58 @@ using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; namespace Microsoft.CodeAnalysis.CodeRefactorings; internal abstract partial class SyntaxEditorBasedCodeRefactoringProvider : CodeRefactoringProvider { - protected static readonly ImmutableArray DefaultFixAllScopes = [FixAllScope.Document, FixAllScope.Project, FixAllScope.Solution]; - protected static readonly ImmutableArray AllFixAllScopes = [FixAllScope.Document, FixAllScope.Project, FixAllScope.Solution, FixAllScope.ContainingType, FixAllScope.ContainingMember]; + protected static readonly ImmutableArray DefaultRefactorAllScopes = [RefactorAllScope.Document, RefactorAllScope.Project, RefactorAllScope.Solution]; + protected static readonly ImmutableArray AllRefactorAllScopes = [RefactorAllScope.Document, RefactorAllScope.Project, RefactorAllScope.Solution, RefactorAllScope.ContainingType, RefactorAllScope.ContainingMember]; - protected abstract ImmutableArray SupportedFixAllScopes { get; } + protected abstract ImmutableArray SupportedRefactorAllScopes { get; } protected virtual CodeActionCleanup Cleanup => CodeActionCleanup.Default; - internal sealed override FixAllProvider? GetFixAllProvider() + public sealed override RefactorAllProvider? GetRefactorAllProvider() { - if (SupportedFixAllScopes.IsEmpty) + if (SupportedRefactorAllScopes.IsEmpty) return null; - return FixAllProvider.Create( - async (fixAllContext, document, fixAllSpans) => - await this.FixAllAsync(document, fixAllSpans, fixAllContext.CodeActionEquivalenceKey, fixAllContext.CancellationToken).ConfigureAwait(false), - SupportedFixAllScopes, + return RefactorAllProvider.Create( + async (refactorAllContext, document, refactorAllSpans) => + await this.RefactorAllAsync(document, refactorAllSpans, refactorAllContext.CodeActionEquivalenceKey, refactorAllContext.CancellationToken).ConfigureAwait(false), + SupportedRefactorAllScopes, this.Cleanup); } - protected Task FixAsync( + protected Task RefactorAsync( Document document, - TextSpan fixAllSpan, + TextSpan refactorAllSpan, string? equivalenceKey, CancellationToken cancellationToken) { - return FixAllWithEditorAsync(document, - editor => FixAllAsync(document, [fixAllSpan], editor, equivalenceKey, cancellationToken), + return RefactorAllWithEditorAsync(document, + editor => RefactorAllAsync(document, [refactorAllSpan], editor, equivalenceKey, cancellationToken), cancellationToken); } - protected Task FixAllAsync( + protected Task RefactorAllAsync( Document document, - Optional> fixAllSpans, + Optional> refactorAllSpans, string? equivalenceKey, CancellationToken cancellationToken) { - return FixAllWithEditorAsync(document, FixAllAsync, cancellationToken); + return RefactorAllWithEditorAsync(document, RefactorAllAsync, cancellationToken); // Local functions - Task FixAllAsync(SyntaxEditor editor) + Task RefactorAllAsync(SyntaxEditor editor) { - // Fix the entire document if there are no sub-spans to fix. - var spans = fixAllSpans.HasValue ? fixAllSpans.Value : [editor.OriginalRoot.FullSpan]; - return this.FixAllAsync(document, spans, editor, equivalenceKey, cancellationToken); + // Refactor the entire document if there are no sub-spans to refactor. + var spans = refactorAllSpans.HasValue ? refactorAllSpans.Value : [editor.OriginalRoot.FullSpan]; + return this.RefactorAllAsync(document, spans, editor, equivalenceKey, cancellationToken); } } - internal static async Task FixAllWithEditorAsync( + internal static async Task RefactorAllWithEditorAsync( Document document, Func editAsync, CancellationToken cancellationToken) @@ -76,9 +75,9 @@ internal static async Task FixAllWithEditorAsync( return document.WithSyntaxRoot(newRoot); } - protected abstract Task FixAllAsync( + protected abstract Task RefactorAllAsync( Document document, - ImmutableArray fixAllSpans, + ImmutableArray refactorAllSpans, SyntaxEditor editor, string? equivalenceKey, CancellationToken cancellationToken); diff --git a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/AnalyzerFilter.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/AnalyzerFilter.cs new file mode 100644 index 00000000000..6fd1ebb342d --- /dev/null +++ b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/AnalyzerFilter.cs @@ -0,0 +1,30 @@ +// 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.CodeAnalysis.Diagnostics; + +[Flags] +internal enum AnalyzerFilter +{ + /// + /// The default 'compiler analyzer' which reports the standard set of compiler diagnostics. + /// + CompilerAnalyzer = 1, + + /// + /// Any other analyzer that is not the default 'compiler analyzer'. + /// + NonCompilerAnalyzer = 2, + + /// + /// Include both compiler and non-compiler analyzers. + /// + All = CompilerAnalyzer | NonCompilerAnalyzer, +} diff --git a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/CodeAnalysisEventSource.Workspaces.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/CodeAnalysisEventSource.Workspaces.cs index bf31bc4a0b0..95ae3c00eea 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/CodeAnalysisEventSource.Workspaces.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/CodeAnalysisEventSource.Workspaces.cs @@ -9,5 +9,5 @@ namespace Microsoft.CodeAnalysis; [EventSource(Name = "Microsoft-CodeAnalysis-Workspaces")] internal sealed partial class CodeAnalysisEventSource { - public static readonly CodeAnalysisEventSource Log = new CodeAnalysisEventSource(); + public static readonly CodeAnalysisEventSource Log = new(); } diff --git a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalyzerInfoCache.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalyzerInfoCache.cs index 99586494d07..4397f6fa10f 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalyzerInfoCache.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalyzerInfoCache.cs @@ -32,7 +32,7 @@ internal sealed partial class DiagnosticAnalyzerInfoCache /// The purpose of this map is to avoid multiple calls to that might return different values /// (they should not but we need a guarantee to function correctly). /// - private readonly ConditionalWeakTable _descriptorsInfo; + private readonly ConditionalWeakTable _descriptorsInfo = new(); /// /// Supported suppressions of each . @@ -44,13 +44,13 @@ internal sealed partial class DiagnosticAnalyzerInfoCache /// The purpose of this map is to avoid multiple calls to that might return different values /// (they should not but we need a guarantee to function correctly). /// - private readonly ConditionalWeakTable _suppressionsInfo; + private readonly ConditionalWeakTable _suppressionsInfo = new(); /// /// Lazily populated map from diagnostic IDs to diagnostic descriptor. /// If same diagnostic ID is reported by multiple descriptors, a null value is stored in the map for that ID. /// - private readonly ConcurrentDictionary _idToDescriptorsMap; + private readonly ConcurrentDictionary _idToDescriptorsMap = []; private sealed class DiagnosticDescriptorsInfo(ImmutableArray supportedDescriptors, bool telemetryAllowed) { @@ -64,25 +64,6 @@ private sealed class SuppressionDescriptorsInfo(ImmutableArray SupportedSuppressions = supportedSuppressions; } - [Export, Shared] - internal sealed class SharedGlobalCache - { - public readonly DiagnosticAnalyzerInfoCache AnalyzerInfoCache = new(); - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public SharedGlobalCache() - { - } - } - - internal DiagnosticAnalyzerInfoCache() - { - _descriptorsInfo = new(); - _suppressionsInfo = new(); - _idToDescriptorsMap = []; - } - /// /// Returns of given . /// diff --git a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs index 00233f4dade..2a9a99a59d3 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs @@ -6,6 +6,7 @@ using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Linq; using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; @@ -202,10 +203,27 @@ void GetLocationInfo(out FileLinePositionSpan originalLineInfo, out FileLinePosi } } - public static DiagnosticData Create(Diagnostic diagnostic, Project project) - => Create(diagnostic, project.Id, project.Language, + [return: NotNullIfNotNull(nameof(diagnostic))] + public static DiagnosticData? Create(Diagnostic? diagnostic, Project project) + { + if (diagnostic is null) + return null; + + var document = project.GetDocument(diagnostic.Location.SourceTree); + if (document != null) + return Create(diagnostic, document); + + if (diagnostic.Location.Kind == LocationKind.ExternalFile) + { + document = project.Documents.FirstOrDefault(d => d.FilePath == diagnostic.Location.GetLineSpan().Path); + if (document != null) + return Create(diagnostic, document); + } + + return Create(diagnostic, project.Id, project.Language, location: new DiagnosticDataLocation(new FileLinePositionSpan(project.FilePath ?? project.Solution.FilePath ?? "", span: default)), additionalLocations: default, additionalProperties: null); + } public static DiagnosticData Create(Diagnostic diagnostic, TextDocument document) { diff --git a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticIdFilter.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticIdFilter.cs new file mode 100644 index 00000000000..ddc7b0c5d9c --- /dev/null +++ b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticIdFilter.cs @@ -0,0 +1,65 @@ +// 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.Runtime.Serialization; + +namespace Microsoft.CodeAnalysis.Diagnostics; + +/// +/// If present, if an analyzer has at least one descriptor in this set, it will be included. +/// Note: this set can include diagnostic IDs from multiple analyzers in it. +/// +/// +/// If present, if all of the descriptors an analyzer has is in this set, it will be excluded. +/// Note: this set can include diagnostic IDs from multiple analyzers in it. +/// +[DataContract] +internal readonly record struct DiagnosticIdFilter( + [property: DataMember(Order = 0)] ImmutableHashSet? IncludedDiagnosticIds, + [property: DataMember(Order = 1)] ImmutableHashSet? ExcludedDiagnosticIds) +{ + public static readonly DiagnosticIdFilter All = default; + + public static DiagnosticIdFilter Include(ImmutableHashSet? includedDiagnosticIds) + => new(includedDiagnosticIds, ExcludedDiagnosticIds: null); + + public static DiagnosticIdFilter Exclude(ImmutableHashSet excludedDiagnosticIds) + => new(IncludedDiagnosticIds: null, excludedDiagnosticIds); + + /// + /// Checks the IDs from a single analyzer's to see if it is + /// allowed by this filter. If this is , this will always return true. If there are any + /// values in , at least one of the IDs must be in that set. If there are any + /// values in , not all of the IDs can be in that set. + /// + public bool Allow(params IEnumerable ids) + { + if (this == All) + return true; + + foreach (var id in ids) + { + // If the ID is in the included set, then that's good enough as the semantics for this type are that as long + // as we see one allowed id, we allow the analyzer. + if (IncludedDiagnosticIds != null && + IncludedDiagnosticIds.Contains(id)) + { + return true; + } + + // If the ID is *not* in the excluded set, then that's good enough as the semantics for this type are that we + // only filter out the analyzers that have *all* their IDs in the excluded set. + if (ExcludedDiagnosticIds != null && + !ExcludedDiagnosticIds.Contains(id)) + { + return true; + } + } + + return false; + } +} diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/DiagnosticKind.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticKind.cs similarity index 100% rename from src/roslyn/src/Features/Core/Portable/Diagnostics/DiagnosticKind.cs rename to src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticKind.cs diff --git a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs index 60d0f3dc455..1a12e2683aa 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs @@ -567,24 +567,4 @@ public static bool IsReportedInDocument(Diagnostic diagnostic, TextDocument targ return false; } - - public static ImmutableArray FilterAnalyzers( - this ImmutableArray analyzers, - ImmutableHashSet analyzerIds) - { - using var _ = PooledDictionary.GetInstance(out var analyzerMap); - foreach (var analyzer in analyzers) - { - // In the case of multiple analyzers with the same ID, we keep the last one. - var analyzerId = analyzer.GetAnalyzerId(); - if (analyzerIds.Contains(analyzerId)) - analyzerMap[analyzerId] = analyzer; - } - - var result = new FixedSizeArrayBuilder(analyzerMap.Count); - foreach (var (_, analyzer) in analyzerMap) - result.Add(analyzer); - - return result.MoveToImmutable(); - } } diff --git a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/HostDiagnosticAnalyzers.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/HostDiagnosticAnalyzers.cs index fb72eccc2dc..f72d4e1095e 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/HostDiagnosticAnalyzers.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/HostDiagnosticAnalyzers.cs @@ -75,18 +75,19 @@ internal HostDiagnosticAnalyzers(IReadOnlyList hostAnalyzerRe public ImmutableDictionary> GetOrCreateHostDiagnosticAnalyzersPerReference(string language) => _hostDiagnosticAnalyzersPerLanguageMap.GetOrAdd(language, CreateHostDiagnosticAnalyzersAndBuildMap); - public ImmutableDictionary> GetDiagnosticDescriptorsPerReference(DiagnosticAnalyzerInfoCache infoCache) + public ImmutableDictionary> GetDiagnosticDescriptorsPerReference( + DiagnosticAnalyzerInfoCache infoCache, + Project? project) { - return ConvertReferenceIdentityToName( - CreateDiagnosticDescriptorsPerReference(infoCache, _lazyHostDiagnosticAnalyzersPerReferenceMap.Value), - _hostAnalyzerReferencesMap); - } + var descriptorsPerReference = project is null + ? CreateDiagnosticDescriptorsPerReference(infoCache, _lazyHostDiagnosticAnalyzersPerReferenceMap.Value) + : CreateDiagnosticDescriptorsPerReference(infoCache, CreateDiagnosticAnalyzersPerReference(project)); - public ImmutableDictionary> GetDiagnosticDescriptorsPerReference(DiagnosticAnalyzerInfoCache infoCache, Project project) - { - var descriptorPerReference = CreateDiagnosticDescriptorsPerReference(infoCache, CreateDiagnosticAnalyzersPerReference(project)); - var map = _hostAnalyzerReferencesMap.AddRange(CreateProjectAnalyzerReferencesMap(project.AnalyzerReferences)); - return ConvertReferenceIdentityToName(descriptorPerReference, map); + var map = project is null + ? _hostAnalyzerReferencesMap + : _hostAnalyzerReferencesMap.AddRange(CreateProjectAnalyzerReferencesMap(project.AnalyzerReferences)); + + return ConvertReferenceIdentityToName(descriptorsPerReference, map); } private static ImmutableDictionary> ConvertReferenceIdentityToName( diff --git a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs index c4a322cdcea..2675e961f7b 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs @@ -14,35 +14,33 @@ namespace Microsoft.CodeAnalysis.Diagnostics; internal interface IRemoteDiagnosticAnalyzerService { - /// - /// Returns the analyzers that are candidates to be de-prioritized to - /// priority for improvement in analyzer - /// execution performance for priority buckets above 'Low' priority. - /// Based on performance measurements, currently only analyzers which register SymbolStart/End actions - /// or SemanticModel actions are considered candidates to be de-prioritized. However, these semantics - /// could be changed in future based on performance measurements. - /// - ValueTask> GetDeprioritizationCandidatesAsync( - Checksum solutionChecksum, ProjectId projectId, ImmutableHashSet analyzerIds, CancellationToken cancellationToken); + ValueTask> ForceRunCodeAnalysisDiagnosticsAsync( + Checksum solutionChecksum, ProjectId projectId, CancellationToken cancellationToken); + + ValueTask IsAnyDiagnosticIdDeprioritizedAsync( + Checksum solutionChecksum, ProjectId projectId, ImmutableArray diagnosticIds, CancellationToken cancellationToken); - ValueTask> ComputeDiagnosticsAsync( - Checksum solutionChecksum, DocumentId documentId, TextSpan? range, - ImmutableHashSet allAnalyzerIds, - ImmutableHashSet syntaxAnalyzersIds, - ImmutableHashSet semanticSpanAnalyzersIds, - ImmutableHashSet semanticDocumentAnalyzersIds, - bool incrementalAnalysis, - bool logPerformanceInfo, + ValueTask> GetDiagnosticsForSpanAsync( + Checksum solutionChecksum, + DocumentId documentId, + TextSpan? range, + DiagnosticIdFilter diagnosticIdFilter, + CodeActionRequestPriority? priority, + DiagnosticKind diagnosticKind, CancellationToken cancellationToken); - ValueTask> ProduceProjectDiagnosticsAsync( + ValueTask> GetDiagnosticsForIdsAsync( Checksum solutionChecksum, ProjectId projectId, - ImmutableHashSet analyzerIds, - ImmutableHashSet? diagnosticIds, ImmutableArray documentIds, + ImmutableHashSet? diagnosticIds, + AnalyzerFilter analyzerFilter, bool includeLocalDocumentDiagnostics, - bool includeNonLocalDocumentDiagnostics, - bool includeProjectNonLocalResult, + CancellationToken cancellationToken); + + ValueTask> GetProjectDiagnosticsForIdsAsync( + Checksum solutionChecksum, ProjectId projectId, + ImmutableHashSet? diagnosticIds, + AnalyzerFilter analyzerFilter, CancellationToken cancellationToken); ValueTask> GetSourceGeneratorDiagnosticsAsync(Checksum solutionChecksum, ProjectId projectId, CancellationToken cancellationToken); @@ -50,11 +48,11 @@ ValueTask> ProduceProjectDiagnosticsAsync( ValueTask> GetDiagnosticDescriptorsAsync( Checksum solutionChecksum, ProjectId projectId, string analyzerReferenceFullPath, string language, CancellationToken cancellationToken); - ValueTask>> GetDiagnosticDescriptorsPerReferenceAsync( + ValueTask> GetCompilationEndDiagnosticDescriptorIdsAsync( Checksum solutionChecksum, CancellationToken cancellationToken); ValueTask>> GetDiagnosticDescriptorsPerReferenceAsync( - Checksum solutionChecksum, ProjectId projectId, CancellationToken cancellationToken); + Checksum solutionChecksum, ProjectId? projectId, CancellationToken cancellationToken); } [DataContract] diff --git a/src/roslyn/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs b/src/roslyn/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs index 6ed520b9011..2404987660e 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; using Roslyn.Utilities; @@ -683,6 +684,14 @@ internal abstract SyntaxNode EnumDeclaration( DeclarationModifiers modifiers = default, IEnumerable? members = null); + /// + /// Creates an extension block declaration + /// + internal abstract SyntaxNode ExtensionBlockDeclaration( + SyntaxNode extensionParameter, + IEnumerable? typeParameters, + IEnumerable members); + /// /// Creates an enum member /// @@ -765,7 +774,7 @@ public SyntaxNode Declaration(ISymbol symbol) modifiers: DeclarationModifiers.From(type), baseType: type.BaseType != null ? TypeExpression(type.BaseType) : null, interfaceTypes: type.Interfaces.Select(TypeExpression), - members: type.GetMembers().Where(CanBeDeclared).Select(Declaration)), + members: GetMembersExceptExtensionImplementations(type).Where(CanBeDeclared).Select(Declaration)), TypeKind.Struct => StructDeclaration( type.IsRecord, type.Name, @@ -773,20 +782,20 @@ public SyntaxNode Declaration(ISymbol symbol) accessibility: type.DeclaredAccessibility, modifiers: DeclarationModifiers.From(type), interfaceTypes: type.Interfaces.Select(TypeExpression), - members: type.GetMembers().Where(CanBeDeclared).Select(Declaration)), + members: type.GetMembers().SelectAsArray(CanBeDeclared, Declaration)), TypeKind.Interface => InterfaceDeclaration( type.Name, type.TypeParameters.Select(TypeParameter), accessibility: type.DeclaredAccessibility, interfaceTypes: type.Interfaces.Select(TypeExpression), - members: type.GetMembers().Where(CanBeDeclared).Select(Declaration)), + members: type.GetMembers().SelectAsArray(CanBeDeclared, Declaration)), TypeKind.Enum => EnumDeclaration( type.Name, underlyingType: type.EnumUnderlyingType is null or { SpecialType: SpecialType.System_Int32 } ? null : TypeExpression(type.EnumUnderlyingType.SpecialType), accessibility: type.DeclaredAccessibility, - members: type.GetMembers().Where(s => s.Kind == SymbolKind.Field).Select(Declaration)), + members: type.GetMembers().SelectAsArray(s => s.Kind == SymbolKind.Field, Declaration)), TypeKind.Delegate => type.GetMembers(WellKnownMemberNames.DelegateInvokeName) is [IMethodSymbol invoke, ..] ? DelegateDeclaration( type.Name, @@ -796,6 +805,10 @@ public SyntaxNode Declaration(ISymbol symbol) accessibility: type.DeclaredAccessibility, modifiers: DeclarationModifiers.From(type)) : null, + TypeKind.Extension when type.ExtensionParameter is { } extensionParameter => ExtensionBlockDeclaration( + ParameterDeclaration(extensionParameter), + typeParameters: type.TypeParameters.Select(TypeParameter), + members: type.GetMembers().Where(CanBeDeclared).Select(Declaration)), _ => null, }; @@ -806,6 +819,49 @@ public SyntaxNode Declaration(ISymbol symbol) } throw new ArgumentException("Symbol cannot be converted to a declaration"); + + static IEnumerable GetMembersExceptExtensionImplementations(INamedTypeSymbol type) + { + var members = type.GetMembers(); + using var _ = PooledHashSet.GetInstance(out var implementationsToHide); + foreach (var nested in type.GetTypeMembers("")) + { + if (nested.IsExtension) + { + foreach (var extensionMember in nested.GetMembers()) + { + if (extensionMember is IMethodSymbol { OriginalDefinition.AssociatedExtensionImplementation: { } toShadow }) + { + implementationsToHide.Add(toShadow); + } + } + } + } + + if (implementationsToHide is null) + { + return members; + } + + using var _2 = ArrayBuilder.GetInstance(out var result); + foreach (var member in members) + { + if (member is IMethodSymbol method) + { + // Hide implementation methods + if (!implementationsToHide.Contains(method.OriginalDefinition)) + { + result.Add(member); + } + } + else + { + result.Add(member); + } + } + + return result.ToImmutableAndClear(); + } } private static bool CanBeDeclared(ISymbol symbol) @@ -832,6 +888,7 @@ private static bool CanBeDeclared(ISymbol symbol) { case MethodKind.Constructor: case MethodKind.SharedConstructor: + case MethodKind.UserDefinedOperator: return true; case MethodKind.Ordinary: return method.CanBeReferencedByName; @@ -849,6 +906,8 @@ private static bool CanBeDeclared(ISymbol symbol) case TypeKind.Enum: case TypeKind.Delegate: return type.CanBeReferencedByName; + case TypeKind.Extension: + return true; } break; diff --git a/src/roslyn/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaEditDistanceWrapper.cs b/src/roslyn/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaEditDistanceWrapper.cs index 9080f943b6c..10b668b5471 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaEditDistanceWrapper.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaEditDistanceWrapper.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Pythia.Api; internal readonly struct PythiaEditDistanceWrapper(string str) : IDisposable { - private readonly EditDistance _underlyingObject = new EditDistance(str); + private readonly EditDistance _underlyingObject = new(str); public double GetEditDistance(string target) => _underlyingObject.GetEditDistance(target); diff --git a/src/roslyn/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.PatternSegment.cs b/src/roslyn/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.PatternSegment.cs index c485e690719..45dc35e5dc1 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.PatternSegment.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.PatternSegment.cs @@ -20,7 +20,7 @@ private struct PatternSegment(string text, bool allowFuzzyMatching) : IDisposabl // Information about the entire piece of text between the dots. For example, if the // text between the dots is 'Get-Keyword', then TotalTextChunk.Text will be 'Get-Keyword' and // TotalTextChunk.CharacterSpans will correspond to 'G', 'et', 'K' and 'eyword'. - public TextChunk TotalTextChunk = new TextChunk(text, allowFuzzyMatching); + public TextChunk TotalTextChunk = new(text, allowFuzzyMatching); // Information about the subwords compromising the total word. For example, if the // text between the dots is 'Get-Keyword', then the subwords will be 'Get' and 'Keyword' diff --git a/src/roslyn/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/roslyn/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index a0961d4c58c..351a1e85e63 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/roslyn/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -1,4 +1,24 @@ *REMOVED*abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.WithModifiers(Microsoft.CodeAnalysis.SyntaxNode declaration, Microsoft.CodeAnalysis.Editing.DeclarationModifiers modifiers) -> Microsoft.CodeAnalysis.SyntaxNode +abstract Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllProvider.GetRefactoringAsync(Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext refactorAllContext) -> System.Threading.Tasks.Task +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext.CancellationToken.get -> System.Threading.CancellationToken +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext.CodeActionEquivalenceKey.get -> string +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext.CodeRefactoringProvider.get -> Microsoft.CodeAnalysis.CodeRefactorings.CodeRefactoringProvider +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext.Document.get -> Microsoft.CodeAnalysis.Document +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext.GetRefactorAllSpansAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task>>> +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext.Progress.get -> System.IProgress +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext.Project.get -> Microsoft.CodeAnalysis.Project +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext.Scope.get -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext.Solution.get -> Microsoft.CodeAnalysis.Solution +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllProvider +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllProvider.RefactorAllProvider() -> void +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope.ContainingMember = 4 -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope.ContainingType = 5 -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope.Custom = 3 -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope.Document = 0 -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope.Project = 1 -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope.Solution = 2 -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope Microsoft.CodeAnalysis.Editing.SyntaxGenerator.WithModifiers(Microsoft.CodeAnalysis.SyntaxNode declaration, Microsoft.CodeAnalysis.Editing.DeclarationModifiers modifiers) -> Microsoft.CodeAnalysis.SyntaxNode Microsoft.CodeAnalysis.Editing.OperatorKind.AdditionAssignment = 27 -> Microsoft.CodeAnalysis.Editing.OperatorKind Microsoft.CodeAnalysis.Editing.OperatorKind.BitwiseAndAssignment = 33 -> Microsoft.CodeAnalysis.Editing.OperatorKind @@ -31,7 +51,11 @@ Microsoft.CodeAnalysis.WorkspaceEventRegistration.Dispose() -> void override Microsoft.CodeAnalysis.WorkspaceEventOptions.Equals(object obj) -> bool override Microsoft.CodeAnalysis.WorkspaceEventOptions.GetHashCode() -> int override Microsoft.CodeAnalysis.WorkspaceEventOptions.ToString() -> string +static Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllProvider.Create(System.Func>, System.Threading.Tasks.Task> refactorAllAsync) -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllProvider +static Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllProvider.Create(System.Func>, System.Threading.Tasks.Task> refactorAllAsync, System.Collections.Immutable.ImmutableArray supportedRefactorAllScopes) -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllProvider static Microsoft.CodeAnalysis.WorkspaceEventOptions.operator !=(Microsoft.CodeAnalysis.WorkspaceEventOptions left, Microsoft.CodeAnalysis.WorkspaceEventOptions right) -> bool static Microsoft.CodeAnalysis.WorkspaceEventOptions.operator ==(Microsoft.CodeAnalysis.WorkspaceEventOptions left, Microsoft.CodeAnalysis.WorkspaceEventOptions right) -> bool static readonly Microsoft.CodeAnalysis.WorkspaceEventOptions.DefaultOptions -> Microsoft.CodeAnalysis.WorkspaceEventOptions -static readonly Microsoft.CodeAnalysis.WorkspaceEventOptions.RequiresMainThreadOptions -> Microsoft.CodeAnalysis.WorkspaceEventOptions \ No newline at end of file +static readonly Microsoft.CodeAnalysis.WorkspaceEventOptions.RequiresMainThreadOptions -> Microsoft.CodeAnalysis.WorkspaceEventOptions +virtual Microsoft.CodeAnalysis.CodeRefactorings.CodeRefactoringProvider.GetRefactorAllProvider() -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllProvider +virtual Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllProvider.GetSupportedRefactorAllScopes() -> System.Collections.Generic.IEnumerable \ No newline at end of file diff --git a/src/roslyn/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictingIdentifierTracker.cs b/src/roslyn/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictingIdentifierTracker.cs index 7af875c8cac..21dc0c0b02f 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictingIdentifierTracker.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictingIdentifierTracker.cs @@ -13,7 +13,7 @@ internal sealed class ConflictingIdentifierTracker(SyntaxToken tokenBeingRenamed /// current identifier tokens that are declaring variables. This should only ever be updated /// via the AddIdentifier and RemoveIdentifier helpers. /// - private readonly Dictionary> _currentIdentifiersInScope = new Dictionary>(identifierComparer); + private readonly Dictionary> _currentIdentifiersInScope = new(identifierComparer); private readonly HashSet _conflictingTokensToReport = []; public IEnumerable ConflictingTokens => _conflictingTokensToReport; diff --git a/src/roslyn/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs b/src/roslyn/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs index 677d048ee36..d207e3a6894 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs @@ -88,7 +88,7 @@ internal static IEnumerable GetDocumentsAffectedByRename(ISymbol symbo } else { - var documentsOfRenameSymbolDeclaration = symbol.Locations.Where(l => l.IsInSource).Select(l => solution.GetRequiredDocument(l.SourceTree!)); + var documentsOfRenameSymbolDeclaration = symbol.Locations.SelectAsArray(l => l.IsInSource, l => solution.GetRequiredDocument(l.SourceTree!)); var projectIdsOfRenameSymbolDeclaration = documentsOfRenameSymbolDeclaration.SelectMany(d => d.GetLinkedDocumentIds()) .Concat(documentsOfRenameSymbolDeclaration.First().Id) diff --git a/src/roslyn/src/Workspaces/Core/Portable/Shared/Extensions/ILanguageMetadataExtensions.cs b/src/roslyn/src/Workspaces/Core/Portable/Shared/Extensions/ILanguageMetadataExtensions.cs index e4376a18de6..967d624f9dc 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Shared/Extensions/ILanguageMetadataExtensions.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Shared/Extensions/ILanguageMetadataExtensions.cs @@ -20,10 +20,10 @@ internal static class ILanguageMetadataExtensions return services.Where(s => s.Metadata.Language == languageName).Select(s => s.Value).FirstOrDefault(); } - public static IEnumerable FilterToSpecificLanguage(this IEnumerable> services, string languageName) + public static ImmutableArray FilterToSpecificLanguage(this IEnumerable> services, string languageName) where TMetadata : ILanguageMetadata { - return services.Where(s => s.Metadata.Language == languageName).Select(s => s.Value); + return services.SelectAsArray(s => s.Metadata.Language == languageName, s => s.Value); } public static ImmutableDictionary>> ToPerLanguageMap(this IEnumerable> services) diff --git a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Host/Documentation/DocumentationProviderServiceFactory.cs b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Host/Documentation/DocumentationProviderServiceFactory.cs index 3179339c11e..5551dac4b39 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Host/Documentation/DocumentationProviderServiceFactory.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Host/Documentation/DocumentationProviderServiceFactory.cs @@ -24,8 +24,7 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) internal sealed class DocumentationProviderService : IDocumentationProviderService { - private readonly ConcurrentDictionary _assemblyPathToDocumentationProviderMap = - new(); + private readonly ConcurrentDictionary _assemblyPathToDocumentationProviderMap = new(); public DocumentationProvider GetDocumentationProvider(string assemblyPath) { diff --git a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Host/Metadata/MetadataServiceFactory.cs b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Host/Metadata/MetadataServiceFactory.cs index 21122abcb22..9a2fec57b15 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Host/Metadata/MetadataServiceFactory.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Host/Metadata/MetadataServiceFactory.cs @@ -24,7 +24,7 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) private sealed class DefaultMetadataService(IDocumentationProviderService documentationService) : IMetadataService { - private readonly MetadataReferenceCache _metadataCache = new MetadataReferenceCache((path, properties) => + private readonly MetadataReferenceCache _metadataCache = new((path, properties) => MetadataReference.CreateFromFile(path, properties, documentationService.GetDocumentationProvider(path))); public PortableExecutableReference GetReference(string resolvedPath, MetadataReferenceProperties properties) diff --git a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.RegularCompilationTracker.cs b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.RegularCompilationTracker.cs index e6d1c1f92ba..7754b7924a1 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.RegularCompilationTracker.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.RegularCompilationTracker.cs @@ -33,7 +33,7 @@ private sealed partial class RegularCompilationTracker : ICompilationTracker private static readonly Func s_logBuildCompilationAsync = state => string.Join(",", state.AssemblyName, state.DocumentStates.Count); - private static readonly CancellableLazy s_lazyNullCompilation = new CancellableLazy((Compilation?)null); + private static readonly CancellableLazy s_lazyNullCompilation = new((Compilation?)null); public ProjectState ProjectState { get; } diff --git a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index 1f9ca11562d..5ce65e7a6c8 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -68,7 +68,7 @@ internal sealed partial class SolutionState private readonly Lazy _lazyAnalyzers; // Mapping from file path to the set of documents that are related to it. - private readonly ConcurrentDictionary> _lazyFilePathToRelatedDocumentIds = new ConcurrentDictionary>(FilePathComparer); + private readonly ConcurrentDictionary> _lazyFilePathToRelatedDocumentIds = new(FilePathComparer); private SolutionState( string? workspaceKind, diff --git a/src/roslyn/src/Workspaces/Core/Portable/Workspace/WorkspaceEventMap.cs b/src/roslyn/src/Workspaces/Core/Portable/Workspace/WorkspaceEventMap.cs index 28ebd8688b8..900d8f599c0 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Workspace/WorkspaceEventMap.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Workspace/WorkspaceEventMap.cs @@ -66,10 +66,10 @@ public sealed class EventHandlerSet(ImmutableArray registries) private readonly ImmutableArray _registries = registries; public static EventHandlerSet Create(WorkspaceEventHandlerAndOptions handlerAndOptions) - => new EventHandlerSet([new Registry(handlerAndOptions)]); + => new([new Registry(handlerAndOptions)]); public EventHandlerSet AddHandler(WorkspaceEventHandlerAndOptions handlerAndOptions) - => new EventHandlerSet(_registries.Add(new Registry(handlerAndOptions))); + => new(_registries.Add(new Registry(handlerAndOptions))); public EventHandlerSet RemoveHandler(WorkspaceEventHandlerAndOptions handlerAndOptions) { diff --git a/src/roslyn/src/Workspaces/CoreTest/BatchFixAllProviderTests.cs b/src/roslyn/src/Workspaces/CoreTest/BatchFixAllProviderTests.cs index 1252e90ead3..b43aa84cc4b 100644 --- a/src/roslyn/src/Workspaces/CoreTest/BatchFixAllProviderTests.cs +++ b/src/roslyn/src/Workspaces/CoreTest/BatchFixAllProviderTests.cs @@ -44,7 +44,7 @@ class TestClass { private sealed class LiteralZeroAnalyzer : DiagnosticAnalyzer { internal static readonly DiagnosticDescriptor Descriptor = - new DiagnosticDescriptor("LiteralZero", "title", "message", "category", DiagnosticSeverity.Warning, isEnabledByDefault: true); + new("LiteralZero", "title", "message", "category", DiagnosticSeverity.Warning, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics => [Descriptor]; diff --git a/src/roslyn/src/Workspaces/CoreTest/Differencing/LongestCommonSubsequenceTests.cs b/src/roslyn/src/Workspaces/CoreTest/Differencing/LongestCommonSubsequenceTests.cs index b299d7dbdea..5675cf36f18 100644 --- a/src/roslyn/src/Workspaces/CoreTest/Differencing/LongestCommonSubsequenceTests.cs +++ b/src/roslyn/src/Workspaces/CoreTest/Differencing/LongestCommonSubsequenceTests.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Differencing.UnitTests; public sealed class LongestCommonSubsequenceTests { - private readonly LongestCommonSubsequenceString lcs = new LongestCommonSubsequenceString(); + private readonly LongestCommonSubsequenceString lcs = new(); private sealed class LongestCommonSubsequenceString : LongestCommonSubsequence { diff --git a/src/roslyn/src/Workspaces/CoreTest/Differencing/TestTreeComparer.cs b/src/roslyn/src/Workspaces/CoreTest/Differencing/TestTreeComparer.cs index aec9f90c374..9265272ae2d 100644 --- a/src/roslyn/src/Workspaces/CoreTest/Differencing/TestTreeComparer.cs +++ b/src/roslyn/src/Workspaces/CoreTest/Differencing/TestTreeComparer.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Differencing.UnitTests; public sealed class TestTreeComparer : TreeComparer { - public static readonly TestTreeComparer Instance = new TestTreeComparer(); + public static readonly TestTreeComparer Instance = new(); private TestTreeComparer() { @@ -50,7 +50,7 @@ protected internal override int GetLabel(TestNode node) => node.Label; protected internal override TextSpan GetSpan(TestNode node) - => new TextSpan(0, 10); + => new(0, 10); protected internal override int TiedToAncestor(int label) => 0; diff --git a/src/roslyn/src/Workspaces/CoreTest/Editing/SyntaxEditorTests.cs b/src/roslyn/src/Workspaces/CoreTest/Editing/SyntaxEditorTests.cs index 0acfee41730..3963f7255e1 100644 --- a/src/roslyn/src/Workspaces/CoreTest/Editing/SyntaxEditorTests.cs +++ b/src/roslyn/src/Workspaces/CoreTest/Editing/SyntaxEditorTests.cs @@ -33,7 +33,7 @@ private void VerifySyntax(SyntaxNode node, string expectedText) where T } private SyntaxEditor GetEditor(SyntaxNode root) - => new SyntaxEditor(root, EmptyWorkspace.Services.SolutionServices); + => new(root, EmptyWorkspace.Services.SolutionServices); [Fact] public void TestReplaceNode() diff --git a/src/roslyn/src/Workspaces/CoreTest/ExtensionOrdererTests.cs b/src/roslyn/src/Workspaces/CoreTest/ExtensionOrdererTests.cs index 5f4b181d57b..a5cd9493725 100644 --- a/src/roslyn/src/Workspaces/CoreTest/ExtensionOrdererTests.cs +++ b/src/roslyn/src/Workspaces/CoreTest/ExtensionOrdererTests.cs @@ -206,7 +206,7 @@ public void TestCycle8() #region Helpers private static Lazy CreateExtension(string? name = null, IEnumerable? before = null, IEnumerable? after = null) - => new Lazy(new OrderableMetadata(name, before: before, after: after)); + => new(new OrderableMetadata(name, before: before, after: after)); private static IEnumerable GetNames(IEnumerable> actual) => actual.Select(i => i.Metadata.Name); diff --git a/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/ExportProviderCache.cs b/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/ExportProviderCache.cs index 1bdc55adfbc..e28b1f25e30 100644 --- a/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/ExportProviderCache.cs +++ b/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/ExportProviderCache.cs @@ -24,8 +24,8 @@ public static class ExportProviderCache private static readonly TestComposition s_defaultHostExportProviderComposition = TestComposition.Empty .AddAssemblies(MefHostServices.DefaultAssemblies) .AddParts(typeof(TestSerializerService.Factory)); - private static readonly Scope _localCompositionScope = new Scope("local"); - private static readonly Scope _remoteCompositionScope = new Scope("remote"); + private static readonly Scope _localCompositionScope = new("local"); + private static readonly Scope _remoteCompositionScope = new("remote"); internal static bool Enabled { get; private set; } diff --git a/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/TestComposition.cs b/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/TestComposition.cs index 4d96f5edfa1..424b08ace4b 100644 --- a/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/TestComposition.cs +++ b/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/TestComposition.cs @@ -21,7 +21,7 @@ namespace Microsoft.CodeAnalysis.Test.Utilities; /// public sealed class TestComposition { - public static readonly TestComposition Empty = new TestComposition([], [], []); + public static readonly TestComposition Empty = new([], [], []); private static readonly Dictionary s_factoryCache = []; diff --git a/src/roslyn/src/Workspaces/CoreTestUtilities/Options/OptionsTestHelpers.cs b/src/roslyn/src/Workspaces/CoreTestUtilities/Options/OptionsTestHelpers.cs index 028b36ae741..a14a0e16cde 100644 --- a/src/roslyn/src/Workspaces/CoreTestUtilities/Options/OptionsTestHelpers.cs +++ b/src/roslyn/src/Workspaces/CoreTestUtilities/Options/OptionsTestHelpers.cs @@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.UnitTests; internal static class OptionsTestHelpers { - public static readonly Option CustomPublicOption = new Option("My Feature", "My Option", defaultValue: true); + public static readonly Option CustomPublicOption = new("My Feature", "My Option", defaultValue: true); // all public options and their non-default values: public static readonly ImmutableArray<(IOption, object)> PublicCustomOptionsWithNonDefaultValues = [(CustomPublicOption, false)]; diff --git a/src/roslyn/src/Workspaces/CoreTestUtilities/Workspaces/TestHostProject`1.cs b/src/roslyn/src/Workspaces/CoreTestUtilities/Workspaces/TestHostProject`1.cs index aa14c3c3fee..ac61e859841 100644 --- a/src/roslyn/src/Workspaces/CoreTestUtilities/Workspaces/TestHostProject`1.cs +++ b/src/roslyn/src/Workspaces/CoreTestUtilities/Workspaces/TestHostProject`1.cs @@ -263,7 +263,7 @@ public ProjectInfo ToProjectInfo() isSubmission: IsSubmission), CompilationOptions, ParseOptions, - documents: Documents.Where(d => !d.IsSourceGenerated).Select(d => d.ToDocumentInfo()), + documents: Documents.SelectAsArray(d => !d.IsSourceGenerated, d => d.ToDocumentInfo()), ProjectReferences, MetadataReferences, AnalyzerReferences, diff --git a/src/roslyn/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlCreation.cs b/src/roslyn/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlCreation.cs index 444cb4e6639..4453b5c4481 100644 --- a/src/roslyn/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlCreation.cs +++ b/src/roslyn/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlCreation.cs @@ -183,7 +183,7 @@ private static XElement CreateCompilationOptionsElement(CompilationOptions optio } private static XElement CreateMetadataReference(string path) - => new XElement(MetadataReferenceElementName, path); + => new(MetadataReferenceElementName, path); protected static XElement CreateDocumentElement( string code, string filePath, string folders = null, ParseOptions parseOptions = null, bool isMarkup = true) diff --git a/src/roslyn/src/Workspaces/MSBuild/BuildHost/BuildHost.cs b/src/roslyn/src/Workspaces/MSBuild/BuildHost/BuildHost.cs index bde0afd2c3e..ec31e8096e6 100644 --- a/src/roslyn/src/Workspaces/MSBuild/BuildHost/BuildHost.cs +++ b/src/roslyn/src/Workspaces/MSBuild/BuildHost/BuildHost.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; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.IO; @@ -18,17 +19,23 @@ namespace Microsoft.CodeAnalysis.MSBuild; internal sealed class BuildHost : IBuildHost { private readonly BuildHostLogger _logger; - private readonly ImmutableDictionary _globalMSBuildProperties; - private readonly string? _binaryLogPath; private readonly RpcServer _server; - private readonly object _gate = new object(); + private readonly object _gate = new(); private ProjectBuildManager? _buildManager; - public BuildHost(BuildHostLogger logger, ImmutableDictionary globalMSBuildProperties, string? binaryLogPath, RpcServer server) + /// + /// The global properties to use for all builds; should not be changed once the is initialized. + /// + private ImmutableDictionary? _globalMSBuildProperties; + + /// + /// The binary log path to use for all builds; should not be changed once the is initialized. + /// + private string? _binaryLogPath; + + public BuildHost(BuildHostLogger logger, RpcServer server) { _logger = logger; - _globalMSBuildProperties = globalMSBuildProperties; - _binaryLogPath = binaryLogPath; _server = server; } @@ -122,6 +129,9 @@ private void CreateBuildManager() if (_buildManager != null) return; + if (_globalMSBuildProperties is null) + throw new InvalidOperationException($"{nameof(ConfigureGlobalState)} should have been called first to set up global state."); + BinaryLogger? logger = null; if (_binaryLogPath != null) @@ -150,6 +160,18 @@ private void EnsureMSBuildLoaded(string projectFilePath) Contract.ThrowIfFalse(TryEnsureMSBuildLoaded(projectFilePath), $"We don't have an MSBuild to use; {nameof(HasUsableMSBuild)} should have been called first to check."); } + public void ConfigureGlobalState(ImmutableDictionary globalProperties, string? binlogPath) + { + lock (_gate) + { + if (_buildManager != null) + throw new InvalidOperationException($"{nameof(_buildManager)} has already been initialized and cannot be changed"); + + _globalMSBuildProperties = globalProperties; + _binaryLogPath = binlogPath; + } + } + /// /// Returns the target ID of the object created for this. /// diff --git a/src/roslyn/src/Workspaces/MSBuild/BuildHost/Program.cs b/src/roslyn/src/Workspaces/MSBuild/BuildHost/Program.cs index a108720e30a..ffe75885208 100644 --- a/src/roslyn/src/Workspaces/MSBuild/BuildHost/Program.cs +++ b/src/roslyn/src/Workspaces/MSBuild/BuildHost/Program.cs @@ -15,25 +15,16 @@ internal static class Program { internal static async Task Main(string[] args) { + // Note: we should limit the data passed through via command line strings, and pass information through IBuildHost.ConfigureGlobalState whenever possible. + // This is because otherwise we might run into escaping issues, or command line length limits. + var pipeOption = new Option("--pipe") { Required = true }; - var propertyOption = new Option("--property") { Arity = ArgumentArity.ZeroOrMore }; - var binaryLogOption = new Option("--binlog") { Required = false }; var localeOption = new Option("--locale") { Required = true }; - var command = new RootCommand { pipeOption, binaryLogOption, propertyOption, localeOption }; + var command = new RootCommand { pipeOption, localeOption }; var parsedArguments = command.Parse(args); var pipeName = parsedArguments.GetValue(pipeOption)!; - var properties = parsedArguments.GetValue(propertyOption)!; - var binaryLogPath = parsedArguments.GetValue(binaryLogOption); var locale = parsedArguments.GetValue(localeOption)!; - var propertiesBuilder = ImmutableDictionary.CreateBuilder(); - - foreach (var property in properties) - { - var propertyParts = property.Split(['='], count: 2); - propertiesBuilder.Add(propertyParts[0], propertyParts[1]); - } - var logger = new BuildHostLogger(Console.Error); try @@ -53,7 +44,7 @@ internal static async Task Main(string[] args) var server = new RpcServer(pipeServer); - var targetObject = server.AddTarget(new BuildHost(logger, propertiesBuilder.ToImmutable(), binaryLogPath, server)); + var targetObject = server.AddTarget(new BuildHost(logger, server)); Contract.ThrowIfFalse(targetObject == 0, "The first object registered should have target 0, which is assumed by the client."); await server.RunAsync().ConfigureAwait(false); diff --git a/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/Contracts/IBuildHost.cs b/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/Contracts/IBuildHost.cs index 45be2011e2c..8310712b7a6 100644 --- a/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/Contracts/IBuildHost.cs +++ b/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/Contracts/IBuildHost.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.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -28,6 +29,12 @@ internal interface IBuildHost /// bool HasUsableMSBuild(string projectOrSolutionFilePath); + /// + /// Called once on a new process to configure some global state. This is used for these rather than passing through command line strings, since these contain data that might + /// contain paths (which can have escaping issues) or could be quite large (which could run into length limits). + /// + void ConfigureGlobalState(ImmutableDictionary globalProperties, string? binlogPath); + Task LoadProjectFileAsync(string projectFilePath, string languageName, CancellationToken cancellationToken); /// diff --git a/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/Contracts/JsonSettings.cs b/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/Contracts/JsonSettings.cs index ee75ca9bf1c..ecd950e716a 100644 --- a/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/Contracts/JsonSettings.cs +++ b/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/Contracts/JsonSettings.cs @@ -12,7 +12,7 @@ internal static class JsonSettings { public static readonly Encoding StreamEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); - public static readonly JsonSerializerSettings SingleLineSerializerSettings = new JsonSerializerSettings + public static readonly JsonSerializerSettings SingleLineSerializerSettings = new() { // Setting Formatting.None ensures each is serialized to it's own line, which we implicitly rely on Formatting = Newtonsoft.Json.Formatting.None, diff --git a/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/RpcServer.cs b/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/RpcServer.cs index e161bc6fa0c..b856c0fa604 100644 --- a/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/RpcServer.cs +++ b/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/RpcServer.cs @@ -27,13 +27,13 @@ namespace Microsoft.CodeAnalysis.MSBuild; internal sealed class RpcServer { private readonly TextWriter _streamWriter; - private readonly SemaphoreSlim _sendingStreamSemaphore = new SemaphoreSlim(initialCount: 1); + private readonly SemaphoreSlim _sendingStreamSemaphore = new(initialCount: 1); private readonly TextReader _streamReader; private readonly ConcurrentDictionary _rpcTargets = []; private volatile int _nextRpcTargetIndex = -1; // We'll start at -1 so the first value becomes zero - private readonly CancellationTokenSource _shutdownTokenSource = new CancellationTokenSource(); + private readonly CancellationTokenSource _shutdownTokenSource = new(); public RpcServer(PipeStream stream) { diff --git a/src/roslyn/src/Workspaces/MSBuild/Core/MSBuild/BuildHostProcessManager.cs b/src/roslyn/src/Workspaces/MSBuild/Core/MSBuild/BuildHostProcessManager.cs index 6fb626a7f08..f0fc0b7b55c 100644 --- a/src/roslyn/src/Workspaces/MSBuild/Core/MSBuild/BuildHostProcessManager.cs +++ b/src/roslyn/src/Workspaces/MSBuild/Core/MSBuild/BuildHostProcessManager.cs @@ -120,6 +120,8 @@ async Task NoLock_GetBuildHostAsync(BuildHostProcessKind build throw new Exception($"BuildHost process exited immediately with {process.ExitCode}"); } + await buildHostProcess.BuildHost.ConfigureGlobalStateAsync(_globalMSBuildProperties, _binaryLogPathProvider?.GetNewLogPath(), cancellationToken).ConfigureAwait(false); + if (buildHostKind != BuildHostProcessKind.NetCore || projectOrSolutionFilePath is null || dotnetPath is not null) @@ -164,7 +166,7 @@ async Task NoLock_GetBuildHostAsync(BuildHostProcessKind build #endif } - internal ProcessStartInfo CreateBuildHostStartInfo(BuildHostProcessKind buildHostKind, string pipeName, string? dotnetPath) + internal static ProcessStartInfo CreateBuildHostStartInfo(BuildHostProcessKind buildHostKind, string pipeName, string? dotnetPath) { return buildHostKind switch { @@ -219,7 +221,7 @@ public async ValueTask DisposeAsync() await process.DisposeAsync().ConfigureAwait(false); } - private ProcessStartInfo CreateDotNetCoreBuildHostStartInfo(string pipeName, string? dotnetPath) + private static ProcessStartInfo CreateDotNetCoreBuildHostStartInfo(string pipeName, string? dotnetPath) { var processStartInfo = new ProcessStartInfo() { @@ -236,7 +238,7 @@ private ProcessStartInfo CreateDotNetCoreBuildHostStartInfo(string pipeName, str AddArgument(processStartInfo, netCoreBuildHostPath); - AppendBuildHostCommandLineArgumentsConfigureProcess(processStartInfo, pipeName); + AppendBuildHostCommandLineArgumentsAndConfigureProcess(processStartInfo, pipeName); return processStartInfo; } @@ -246,7 +248,7 @@ internal static string GetNetCoreBuildHostPath() return GetBuildHostPath("BuildHost-netcore", "Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.dll"); } - private ProcessStartInfo CreateDotNetFrameworkBuildHostStartInfo(string pipeName) + private static ProcessStartInfo CreateDotNetFrameworkBuildHostStartInfo(string pipeName) { var netFrameworkBuildHost = GetDotNetFrameworkBuildHostPath(); var processStartInfo = new ProcessStartInfo() @@ -254,12 +256,12 @@ private ProcessStartInfo CreateDotNetFrameworkBuildHostStartInfo(string pipeName FileName = netFrameworkBuildHost, }; - AppendBuildHostCommandLineArgumentsConfigureProcess(processStartInfo, pipeName); + AppendBuildHostCommandLineArgumentsAndConfigureProcess(processStartInfo, pipeName); return processStartInfo; } - private ProcessStartInfo CreateMonoBuildHostStartInfo(string pipeName) + private static ProcessStartInfo CreateMonoBuildHostStartInfo(string pipeName) { var processStartInfo = new ProcessStartInfo { @@ -268,7 +270,7 @@ private ProcessStartInfo CreateMonoBuildHostStartInfo(string pipeName) AddArgument(processStartInfo, GetDotNetFrameworkBuildHostPath()); - AppendBuildHostCommandLineArgumentsConfigureProcess(processStartInfo, pipeName); + AppendBuildHostCommandLineArgumentsAndConfigureProcess(processStartInfo, pipeName); return processStartInfo; } @@ -305,23 +307,11 @@ private static string GetBuildHostPath(string contentFolderName, string assembly return buildHostPath; } - private void AppendBuildHostCommandLineArgumentsConfigureProcess(ProcessStartInfo processStartInfo, string pipeName) + private static void AppendBuildHostCommandLineArgumentsAndConfigureProcess(ProcessStartInfo processStartInfo, string pipeName) { AddArgument(processStartInfo, "--pipe"); AddArgument(processStartInfo, pipeName); - foreach (var globalMSBuildProperty in _globalMSBuildProperties) - { - AddArgument(processStartInfo, "--property"); - AddArgument(processStartInfo, globalMSBuildProperty.Key + '=' + globalMSBuildProperty.Value); - } - - if (_binaryLogPathProvider?.GetNewLogPath() is string binaryLogPath) - { - AddArgument(processStartInfo, "--binlog"); - AddArgument(processStartInfo, binaryLogPath); - } - AddArgument(processStartInfo, "--locale"); AddArgument(processStartInfo, System.Globalization.CultureInfo.CurrentUICulture.Name); diff --git a/src/roslyn/src/Workspaces/MSBuild/Core/Rpc/RemoteBuildHost.cs b/src/roslyn/src/Workspaces/MSBuild/Core/Rpc/RemoteBuildHost.cs index 6edb429bfab..72df5327bc6 100644 --- a/src/roslyn/src/Workspaces/MSBuild/Core/Rpc/RemoteBuildHost.cs +++ b/src/roslyn/src/Workspaces/MSBuild/Core/Rpc/RemoteBuildHost.cs @@ -2,6 +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; +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -37,6 +39,10 @@ public RemoteBuildHost(RpcClient client) public Task HasUsableMSBuildAsync(string projectOrSolutionFilePath, CancellationToken cancellationToken) => _client.InvokeAsync(BuildHostTargetObject, nameof(IBuildHost.HasUsableMSBuild), parameters: [projectOrSolutionFilePath], cancellationToken); + /// + public Task ConfigureGlobalStateAsync(ImmutableDictionary globalProperties, string? binlogPath, CancellationToken cancellationToken) + => _client.InvokeAsync(BuildHostTargetObject, nameof(IBuildHost.ConfigureGlobalState), parameters: [globalProperties, binlogPath], cancellationToken); + public async Task LoadProjectFileAsync(string projectFilePath, string languageName, CancellationToken cancellationToken) { var remoteProjectFileTargetObject = await _client.InvokeAsync(BuildHostTargetObject, nameof(IBuildHost.LoadProjectFileAsync), parameters: [projectFilePath, languageName], cancellationToken).ConfigureAwait(false); @@ -61,5 +67,4 @@ public async Task LoadProjectAsync(string projectFilePath, st public Task ShutdownAsync(CancellationToken cancellationToken) => _client.InvokeAsync(BuildHostTargetObject, nameof(IBuildHost.ShutdownAsync), parameters: [], cancellationToken); - } diff --git a/src/roslyn/src/Workspaces/MSBuild/Core/Rpc/RpcClient.cs b/src/roslyn/src/Workspaces/MSBuild/Core/Rpc/RpcClient.cs index cc93ceb6ba2..ee14f340a91 100644 --- a/src/roslyn/src/Workspaces/MSBuild/Core/Rpc/RpcClient.cs +++ b/src/roslyn/src/Workspaces/MSBuild/Core/Rpc/RpcClient.cs @@ -30,13 +30,13 @@ internal sealed class RpcClient /// /// A semaphore taken to synchronize all writes to . /// - private readonly SemaphoreSlim _streamWritingSemaphore = new SemaphoreSlim(initialCount: 1); + private readonly SemaphoreSlim _streamWritingSemaphore = new(initialCount: 1); private readonly TextReader _receivingStreamReader; private readonly ConcurrentDictionary, System.Type? expectedReturnType)> _outstandingRequests = []; private volatile int _nextRequestId = 0; - private readonly CancellationTokenSource _shutdownTokenSource = new CancellationTokenSource(); + private readonly CancellationTokenSource _shutdownTokenSource = new(); public RpcClient(PipeStream stream) { diff --git a/src/roslyn/src/Workspaces/MSBuild/Test/BuildHostProcessManagerTests.cs b/src/roslyn/src/Workspaces/MSBuild/Test/BuildHostProcessManagerTests.cs index 4a3bc61b6e0..a3d0a1de06c 100644 --- a/src/roslyn/src/Workspaces/MSBuild/Test/BuildHostProcessManagerTests.cs +++ b/src/roslyn/src/Workspaces/MSBuild/Test/BuildHostProcessManagerTests.cs @@ -17,8 +17,7 @@ public sealed class BuildHostProcessManagerTests [Fact] public void ProcessStartInfo_ForNetCore_RollsForwardToLatestPreview() { - var processStartInfo = new BuildHostProcessManager() - .CreateBuildHostStartInfo(BuildHostProcessKind.NetCore, pipeName: "", dotnetPath: null); + var processStartInfo = BuildHostProcessManager.CreateBuildHostStartInfo(BuildHostProcessKind.NetCore, pipeName: "", dotnetPath: null); #if NET var rollForwardIndex = processStartInfo.ArgumentList.IndexOf("--roll-forward"); @@ -34,8 +33,7 @@ public void ProcessStartInfo_ForNetCore_RollsForwardToLatestPreview() [Fact] public void ProcessStartInfo_ForNetCore_LaunchesDotNetCLI() { - var processStartInfo = new BuildHostProcessManager() - .CreateBuildHostStartInfo(BuildHostProcessKind.NetCore, pipeName: "", dotnetPath: null); + var processStartInfo = BuildHostProcessManager.CreateBuildHostStartInfo(BuildHostProcessKind.NetCore, pipeName: "", dotnetPath: null); Assert.StartsWith("dotnet", processStartInfo.FileName); } @@ -43,8 +41,7 @@ public void ProcessStartInfo_ForNetCore_LaunchesDotNetCLI() [Fact] public void ProcessStartInfo_ForMono_LaunchesMono() { - var processStartInfo = new BuildHostProcessManager() - .CreateBuildHostStartInfo(BuildHostProcessKind.Mono, pipeName: "", dotnetPath: null); + var processStartInfo = BuildHostProcessManager.CreateBuildHostStartInfo(BuildHostProcessKind.Mono, pipeName: "", dotnetPath: null); Assert.Equal("mono", processStartInfo.FileName); } @@ -52,35 +49,11 @@ public void ProcessStartInfo_ForMono_LaunchesMono() [Fact] public void ProcessStartInfo_ForNetFramework_LaunchesBuildHost() { - var processStartInfo = new BuildHostProcessManager() - .CreateBuildHostStartInfo(BuildHostProcessKind.NetFramework, pipeName: "", dotnetPath: null); + var processStartInfo = BuildHostProcessManager.CreateBuildHostStartInfo(BuildHostProcessKind.NetFramework, pipeName: "", dotnetPath: null); Assert.EndsWith("Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.exe", processStartInfo.FileName); } - [Theory] - [InlineData(BuildHostProcessKind.NetFramework)] - [InlineData(BuildHostProcessKind.NetCore)] - [InlineData(BuildHostProcessKind.Mono)] - internal void ProcessStartInfo_PassesBinLogPath(BuildHostProcessKind buildHostKind) - { - const string BinaryLogPath = "test.binlog"; - - var binLogPathProviderMock = new Mock(MockBehavior.Strict); - binLogPathProviderMock.Setup(m => m.GetNewLogPath()).Returns(BinaryLogPath); - - var processStartInfo = new BuildHostProcessManager(binaryLogPathProvider: binLogPathProviderMock.Object) - .CreateBuildHostStartInfo(buildHostKind, pipeName: "", dotnetPath: null); - -#if NET - var binlogIndex = processStartInfo.ArgumentList.IndexOf("--binlog"); - Assert.True(binlogIndex >= 0); - Assert.Equal(BinaryLogPath, processStartInfo.ArgumentList[binlogIndex + 1]); -#else - Assert.Contains($"--binlog {BinaryLogPath}", processStartInfo.Arguments); -#endif - } - [Theory] [InlineData(BuildHostProcessKind.NetFramework)] [InlineData(BuildHostProcessKind.NetCore)] @@ -89,8 +62,7 @@ internal void ProcessStartInfo_PassesPipeName(BuildHostProcessKind buildHostKind { const string PipeName = "TestPipe"; - var processStartInfo = new BuildHostProcessManager() - .CreateBuildHostStartInfo(buildHostKind, PipeName, dotnetPath: null); + var processStartInfo = BuildHostProcessManager.CreateBuildHostStartInfo(buildHostKind, PipeName, dotnetPath: null); #if NET var binlogIndex = processStartInfo.ArgumentList.IndexOf("--pipe"); @@ -110,8 +82,7 @@ internal void ProcessStartInfo_PassesLocale(BuildHostProcessKind buildHostKind) { const string Locale = "de-DE"; - var processStartInfo = new BuildHostProcessManager() - .CreateBuildHostStartInfo(buildHostKind, pipeName: "", dotnetPath: null); + var processStartInfo = BuildHostProcessManager.CreateBuildHostStartInfo(buildHostKind, pipeName: "", dotnetPath: null); #if NET var localeIndex = processStartInfo.ArgumentList.IndexOf("--locale"); @@ -121,42 +92,4 @@ internal void ProcessStartInfo_PassesLocale(BuildHostProcessKind buildHostKind) Assert.Contains($"--locale {Locale}", processStartInfo.Arguments); #endif } - - [Theory] - [InlineData(BuildHostProcessKind.NetFramework)] - [InlineData(BuildHostProcessKind.NetCore)] - [InlineData(BuildHostProcessKind.Mono)] - internal void ProcessStartInfo_PassesProperties(BuildHostProcessKind buildHostKind) - { - var globalMSBuildProperties = new Dictionary - { - ["TestKey1"] = "TestValue1", - ["TestKey2"] = "TestValue2", - }.ToImmutableDictionary(); - - var buildHostProcessManager = new BuildHostProcessManager(globalMSBuildProperties); - - var processStartInfo = buildHostProcessManager.CreateBuildHostStartInfo(buildHostKind, pipeName: "", dotnetPath: null); - -#if NET - foreach (var kvp in globalMSBuildProperties) - { - var propertyArgument = GetPropertyArgument(kvp); - var argumentIndex = processStartInfo.ArgumentList.IndexOf(propertyArgument); - Assert.True(argumentIndex >= 0); - Assert.Equal("--property", processStartInfo.ArgumentList[argumentIndex - 1]); - } -#else - foreach (var kvp in globalMSBuildProperties) - { - var propertyArgument = GetPropertyArgument(kvp); - Assert.Contains($"--property {propertyArgument}", processStartInfo.Arguments); - } -#endif - - static string GetPropertyArgument(KeyValuePair property) - { - return $"{property.Key}={property.Value}"; - } - } } diff --git a/src/roslyn/src/Workspaces/MSBuild/Test/NewlyCreatedProjectsFromDotNetNew.cs b/src/roslyn/src/Workspaces/MSBuild/Test/NewlyCreatedProjectsFromDotNetNew.cs index 3b239de1cc6..da5b88390c2 100644 --- a/src/roslyn/src/Workspaces/MSBuild/Test/NewlyCreatedProjectsFromDotNetNew.cs +++ b/src/roslyn/src/Workspaces/MSBuild/Test/NewlyCreatedProjectsFromDotNetNew.cs @@ -62,7 +62,14 @@ public NewlyCreatedProjectsFromDotNetNew(ITestOutputHelper testOutput) : base(te [ConditionalTheory(typeof(DotNetSdkMSBuildInstalled))] [MemberData(nameof(GetCSharpProjectTemplateNames), DisableDiscoveryEnumeration = false)] public Task ValidateCSharpTemplateProjects(string templateName) - => AssertTemplateProjectLoadsCleanlyAsync(templateName, LanguageNames.CSharp); + { + if (templateName is "blazor" or "blazorwasm") + { + // https://github.com/dotnet/roslyn/issues/80263 + return Task.CompletedTask; + } + return AssertTemplateProjectLoadsCleanlyAsync(templateName, LanguageNames.CSharp); + } [ConditionalTheory(typeof(DotNetSdkMSBuildInstalled))] [MemberData(nameof(GetVisualBasicProjectTemplateNames), DisableDiscoveryEnumeration = false)] @@ -218,6 +225,7 @@ async Task AssertProjectLoadsCleanlyAsync(string projectFilePath, string[] ignor AssertEx.Empty(workspace.Diagnostics, $"The following workspace diagnostics are being reported for the template."); var compilation = await project.GetRequiredCompilationAsync(CancellationToken.None); + AssertEx.Empty(await project.GetSourceGeneratorDiagnosticsAsync(CancellationToken.None), $"The following source generator diagnostics are being reported for the template."); // Unnecessary using directives are reported with a severity of Hidden var nonHiddenDiagnostics = compilation!.GetDiagnostics() diff --git a/src/roslyn/src/Workspaces/Remote/Core/ExportProviderBuilder.cs b/src/roslyn/src/Workspaces/Remote/Core/ExportProviderBuilder.cs index 01ea9b3af13..4096efe5c4c 100644 --- a/src/roslyn/src/Workspaces/Remote/Core/ExportProviderBuilder.cs +++ b/src/roslyn/src/Workspaces/Remote/Core/ExportProviderBuilder.cs @@ -11,11 +11,9 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Composition; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; @@ -80,6 +78,10 @@ private async Task GetCompositionConfigurationAsync(Canc new AttributedPartDiscoveryV1(Resolver)); var parts = await discovery.CreatePartsAsync(AssemblyPaths, progress: null, cancellationToken).ConfigureAwait(false); + + // Work around https://github.com/microsoft/vs-mef/issues/620 -- parts might be incomplete if the cancellationToken was cancelled + cancellationToken.ThrowIfCancellationRequested(); + var catalog = ComposableCatalog.Create(Resolver) .AddParts(parts) .WithCompositionService(); // Makes an ICompositionService export available to MEF parts to import diff --git a/src/roslyn/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs b/src/roslyn/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs index aec8f687cb4..ab21fb5e4d7 100644 --- a/src/roslyn/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs +++ b/src/roslyn/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs @@ -54,7 +54,7 @@ internal sealed class ProjectIdFormatter : IMessagePackFormatter /// to only serialize or deserialize it's DebugName once. Additionally, this cache allows /// the Deserialization code to only construct the ProjectID a single time. /// - private readonly ConcurrentDictionary _projectIds = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _projectIds = new(); public ProjectId? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) { diff --git a/src/roslyn/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspaceManager.cs b/src/roslyn/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspaceManager.cs index 6a0d9a2c672..48bcac81ede 100644 --- a/src/roslyn/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspaceManager.cs +++ b/src/roslyn/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspaceManager.cs @@ -50,7 +50,7 @@ internal class RemoteWorkspaceManager return new RemoteWorkspaceManager(CreateAssetCache); static SolutionAssetCache CreateAssetCache(RemoteWorkspace workspace) - => new SolutionAssetCache(workspace, cleanupInterval: TimeSpan.FromSeconds(30), purgeAfter: TimeSpan.FromMinutes(1)); + => new(workspace, cleanupInterval: TimeSpan.FromSeconds(30), purgeAfter: TimeSpan.FromMinutes(1)); }); internal static RemoteWorkspaceManager Default => s_default.Value; diff --git a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs deleted file mode 100644 index a8fbc902f21..00000000000 --- a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs +++ /dev/null @@ -1,493 +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.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Collections; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Diagnostics.Telemetry; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Telemetry; -using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.Workspaces.Diagnostics; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Remote.Diagnostics; - -internal sealed class DiagnosticComputer -{ - /// - /// Cache of and a map from analyzer IDs to s - /// for all analyzers for the last project to be analyzed. The instance is - /// shared between all the following document analyses modes for the project: - /// - /// Span-based analysis for active document (lightbulb) - /// Background analysis for active and open documents. - /// - /// NOTE: We do not re-use this cache for project analysis as it leads to significant memory increase in the OOP - /// process. Additionally, we only store the cache entry for the last project to be analyzed instead of maintaining - /// a CWT keyed off each project in the solution, as the CWT does not seem to drop entries until ForceGC happens, - /// leading to significant memory pressure when there are large number of open documents across different projects - /// to be analyzed by background analysis. - /// - private static CompilationWithAnalyzersCacheEntry? s_compilationWithAnalyzersCache = null; - - /// - /// Static gate controlling access to following static fields: - /// - - /// - private static readonly object s_gate = new(); - - /// - /// Solution checksum for the diagnostic request. - /// We use this checksum and the of the diagnostic request as the key - /// to the . - /// - private readonly Checksum _solutionChecksum; - - private readonly TextDocument? _document; - private readonly Project _project; - private readonly TextSpan? _span; - private readonly AnalysisKind? _analysisKind; - private readonly IPerformanceTrackerService? _performanceTracker; - private readonly DiagnosticAnalyzerInfoCache _analyzerInfoCache; - private readonly HostWorkspaceServices _hostWorkspaceServices; - - private DiagnosticComputer( - TextDocument? document, - Project project, - Checksum solutionChecksum, - TextSpan? span, - AnalysisKind? analysisKind, - DiagnosticAnalyzerInfoCache analyzerInfoCache, - HostWorkspaceServices hostWorkspaceServices) - { - _document = document; - _project = project; - _solutionChecksum = solutionChecksum; - _span = span; - _analysisKind = analysisKind; - _analyzerInfoCache = analyzerInfoCache; - _hostWorkspaceServices = hostWorkspaceServices; - _performanceTracker = project.Solution.Services.GetService(); - } - - public static Task GetDiagnosticsAsync( - TextDocument? document, - Project project, - Checksum solutionChecksum, - TextSpan? span, - ImmutableArray projectAnalyzerIds, - ImmutableArray hostAnalyzerIds, - AnalysisKind? analysisKind, - DiagnosticAnalyzerInfoCache analyzerInfoCache, - HostWorkspaceServices hostWorkspaceServices, - bool logPerformanceInfo, - bool getTelemetryInfo, - CancellationToken cancellationToken) - { - // PERF: Due to the concept of InFlight solution snapshots in OOP process, we might have been - // handed a Project instance that does not match the Project instance corresponding to our - // cached CompilationWithAnalyzers instance, while the underlying Solution checksum matches - // for our cached entry and the incoming request. - // We detect this case upfront here and re-use the cached CompilationWithAnalyzers and Project - // instance for diagnostic computation, thus improving the performance of analyzer execution. - // This is an important performance optimization for lightbulb diagnostic computation. - // See https://github.com/dotnet/roslyn/issues/66968 for details. - lock (s_gate) - { - if (s_compilationWithAnalyzersCache?.SolutionChecksum == solutionChecksum && - s_compilationWithAnalyzersCache.Project.Id == project.Id && - s_compilationWithAnalyzersCache.Project != project) - { - project = s_compilationWithAnalyzersCache.Project; - if (document != null) - document = project.GetTextDocument(document.Id); - } - } - - // We execute explicit, user-invoked diagnostics requests with higher priority compared to implicit requests - // from clients such as editor diagnostic tagger to show squiggles, background analysis to populate the error list, etc. - var diagnosticsComputer = new DiagnosticComputer(document, project, solutionChecksum, span, analysisKind, analyzerInfoCache, hostWorkspaceServices); - return diagnosticsComputer.GetDiagnosticsAsync( - projectAnalyzerIds, hostAnalyzerIds, logPerformanceInfo, getTelemetryInfo, cancellationToken); - } - - private async Task GetDiagnosticsAsync( - ImmutableArray projectAnalyzerIds, - ImmutableArray hostAnalyzerIds, - bool logPerformanceInfo, - bool getTelemetryInfo, - CancellationToken cancellationToken) - { - var (compilationWithAnalyzers, projectAnalyzerToIdMap, hostAnalyzerToIdMap) = await GetOrCreateCompilationWithAnalyzersAsync(cancellationToken).ConfigureAwait(false); - if (compilationWithAnalyzers == null) - return DiagnosticAnalysisResults.Empty; - - var (projectAnalyzers, hostAnalyzers) = GetAnalyzers(projectAnalyzerToIdMap, hostAnalyzerToIdMap, projectAnalyzerIds, hostAnalyzerIds); - if (projectAnalyzers.IsEmpty && hostAnalyzers.IsEmpty) - return DiagnosticAnalysisResults.Empty; - - if (_document == null) - { - if (projectAnalyzers.Length < compilationWithAnalyzers.ProjectAnalyzers.Length) - { - Contract.ThrowIfFalse(projectAnalyzers.Length > 0 || compilationWithAnalyzers.HostCompilationWithAnalyzers is not null); - - // PERF: Generate a new CompilationWithAnalyzers with trimmed analyzers for non-document analysis case. - compilationWithAnalyzers = new CompilationWithAnalyzersPair( - projectAnalyzers.Any() ? compilationWithAnalyzers.ProjectCompilation!.WithAnalyzers(projectAnalyzers, compilationWithAnalyzers.ProjectCompilationWithAnalyzers!.AnalysisOptions) : null, - compilationWithAnalyzers.HostCompilationWithAnalyzers); - } - - if (hostAnalyzers.Length < compilationWithAnalyzers.HostAnalyzers.Length) - { - Contract.ThrowIfFalse(hostAnalyzers.Length > 0 || compilationWithAnalyzers.ProjectCompilationWithAnalyzers is not null); - - // PERF: Generate a new CompilationWithAnalyzers with trimmed analyzers for non-document analysis case. - compilationWithAnalyzers = new CompilationWithAnalyzersPair( - compilationWithAnalyzers.ProjectCompilationWithAnalyzers, - hostAnalyzers.Any() ? compilationWithAnalyzers.HostCompilation!.WithAnalyzers(hostAnalyzers, compilationWithAnalyzers.HostCompilationWithAnalyzers!.AnalysisOptions) : null); - } - } - - var skippedAnalyzersInfo = _project.Solution.SolutionState.Analyzers.GetSkippedAnalyzersInfo( - _project.State, _analyzerInfoCache); - - return await AnalyzeAsync(compilationWithAnalyzers, projectAnalyzerToIdMap, hostAnalyzerToIdMap, projectAnalyzers, hostAnalyzers, skippedAnalyzersInfo, - logPerformanceInfo, getTelemetryInfo, cancellationToken).ConfigureAwait(false); - } - - private async Task AnalyzeAsync( - CompilationWithAnalyzersPair compilationWithAnalyzers, - BidirectionalMap projectAnalyzerToIdMap, - BidirectionalMap hostAnalyzerToIdMap, - ImmutableArray projectAnalyzers, - ImmutableArray hostAnalyzers, - SkippedHostAnalyzersInfo skippedAnalyzersInfo, - bool logPerformanceInfo, - bool getTelemetryInfo, - CancellationToken cancellationToken) - { - var documentAnalysisScope = _document != null - ? new DocumentAnalysisScope(_document, _span, projectAnalyzers, hostAnalyzers, _analysisKind!.Value) - : null; - - var (analysisResult, additionalPragmaSuppressionDiagnostics) = await compilationWithAnalyzers.GetAnalysisResultAsync( - documentAnalysisScope, _project, _analyzerInfoCache, cancellationToken).ConfigureAwait(false); - - if (logPerformanceInfo && _performanceTracker != null) - { - // Only log telemetry snapshot is we have an active telemetry session, - // i.e. user has not opted out of reporting telemetry. - var telemetryService = _hostWorkspaceServices.GetRequiredService(); - if (telemetryService.HasActiveSession) - { - // +1 to include project itself - var unitCount = 1; - if (documentAnalysisScope == null) - unitCount += _project.DocumentIds.Count; - - var performanceInfo = analysisResult?.MergedAnalyzerTelemetryInfo.ToAnalyzerPerformanceInfo(_analyzerInfoCache) ?? []; - _performanceTracker.AddSnapshot(performanceInfo, unitCount, forSpanAnalysis: _span.HasValue); - } - } - - var builderMap = ImmutableDictionary.Empty; - if (analysisResult is not null) - { - builderMap = builderMap.AddRange(await analysisResult.ToResultBuilderMapAsync( - additionalPragmaSuppressionDiagnostics, documentAnalysisScope, - _project, projectAnalyzers, hostAnalyzers, skippedAnalyzersInfo, cancellationToken).ConfigureAwait(false)); - } - - var telemetry = getTelemetryInfo - ? GetTelemetryInfo(analysisResult, projectAnalyzers, hostAnalyzers, projectAnalyzerToIdMap, hostAnalyzerToIdMap) - : []; - - return new DiagnosticAnalysisResults(Dehydrate(builderMap, projectAnalyzerToIdMap, hostAnalyzerToIdMap), telemetry); - } - - private static ImmutableArray<(string analyzerId, DiagnosticMap diagnosticMap)> Dehydrate( - ImmutableDictionary builderMap, - BidirectionalMap projectAnalyzerToIdMap, - BidirectionalMap hostAnalyzerToIdMap) - { - var diagnostics = new FixedSizeArrayBuilder<(string analyzerId, DiagnosticMap diagnosticMap)>(builderMap.Count); - - foreach (var (analyzer, analyzerResults) in builderMap) - { - var analyzerId = GetAnalyzerId(projectAnalyzerToIdMap, hostAnalyzerToIdMap, analyzer); - - diagnostics.Add((analyzerId, - new DiagnosticMap( - analyzerResults.SyntaxLocals.SelectAsArray(entry => (entry.Key, entry.Value)), - analyzerResults.SemanticLocals.SelectAsArray(entry => (entry.Key, entry.Value)), - analyzerResults.NonLocals.SelectAsArray(entry => (entry.Key, entry.Value)), - analyzerResults.Others))); - } - - return diagnostics.MoveToImmutable(); - } - - private static ImmutableArray<(string analyzerId, AnalyzerTelemetryInfo)> GetTelemetryInfo( - AnalysisResultPair? analysisResult, - ImmutableArray projectAnalyzers, - ImmutableArray hostAnalyzers, - BidirectionalMap projectAnalyzerToIdMap, - BidirectionalMap hostAnalyzerToIdMap) - { - Func shouldInclude; - if (projectAnalyzers.Length < (analysisResult?.ProjectAnalysisResult?.AnalyzerTelemetryInfo.Count ?? 0) - || hostAnalyzers.Length < (analysisResult?.HostAnalysisResult?.AnalyzerTelemetryInfo.Count ?? 0)) - { - // Filter the telemetry info to the executed analyzers. - using var _1 = PooledHashSet.GetInstance(out var analyzersSet); - analyzersSet.AddRange(projectAnalyzers); - analyzersSet.AddRange(hostAnalyzers); - - shouldInclude = analyzer => analyzersSet.Contains(analyzer); - } - else - { - shouldInclude = _ => true; - } - - using var _2 = ArrayBuilder<(string analyzerId, AnalyzerTelemetryInfo)>.GetInstance(out var telemetryBuilder); - if (analysisResult is not null) - { - foreach (var (analyzer, analyzerTelemetry) in analysisResult.MergedAnalyzerTelemetryInfo) - { - if (shouldInclude(analyzer)) - { - var analyzerId = GetAnalyzerId(projectAnalyzerToIdMap, hostAnalyzerToIdMap, analyzer); - telemetryBuilder.Add((analyzerId, analyzerTelemetry)); - } - } - } - - return telemetryBuilder.ToImmutableAndClear(); - } - - private static string GetAnalyzerId(BidirectionalMap analyzerMap1, BidirectionalMap analyzerMap2, DiagnosticAnalyzer analyzer) - { - var analyzerId = analyzerMap1.GetKeyOrDefault(analyzer) ?? analyzerMap2.GetKeyOrDefault(analyzer); - Contract.ThrowIfNull(analyzerId); - - return analyzerId; - } - - private static (ImmutableArray projectAnalyzers, ImmutableArray hostAnalyzers) GetAnalyzers(BidirectionalMap projectAnalyzerMap, BidirectionalMap hostAnalyzerMap, ImmutableArray projectAnalyzerIds, ImmutableArray hostAnalyzerIds) - { - // TODO: this probably need to be cached as well in analyzer service? - var projectBuilder = ImmutableArray.CreateBuilder(); - var hostBuilder = ImmutableArray.CreateBuilder(); - - foreach (var analyzerId in projectAnalyzerIds) - { - if (projectAnalyzerMap.TryGetValue(analyzerId, out var analyzer)) - { - projectBuilder.Add(analyzer); - } - } - - foreach (var analyzerId in hostAnalyzerIds) - { - if (hostAnalyzerMap.TryGetValue(analyzerId, out var analyzer)) - { - hostBuilder.Add(analyzer); - } - } - - var projectAnalyzers = projectBuilder.ToImmutableAndClear(); - - if (hostAnalyzerIds.Any()) - { - // If any host analyzers are active, make sure to also include any project diagnostic suppressors - var projectSuppressors = projectAnalyzers.WhereAsArray(static a => a is DiagnosticSuppressor); - // Make sure to remove any project suppressors already in the host analyzer array so we don't end up with - // duplicates. - hostBuilder.RemoveRange(projectSuppressors); - hostBuilder.AddRange(projectSuppressors); - } - - return (projectAnalyzers, hostBuilder.ToImmutableAndClear()); - } - - private async Task<(CompilationWithAnalyzersPair? compilationWithAnalyzers, BidirectionalMap projectAnalyzerToIdMap, BidirectionalMap hostAnalyzerToIdMap)> GetOrCreateCompilationWithAnalyzersAsync(CancellationToken cancellationToken) - { - var cacheEntry = await GetOrCreateCacheEntryAsync().ConfigureAwait(false); - return (cacheEntry.CompilationWithAnalyzers, cacheEntry.ProjectAnalyzerToIdMap, cacheEntry.HostAnalyzerToIdMap); - - async Task GetOrCreateCacheEntryAsync() - { - if (_document == null) - { - // Only use cache for document analysis. - return await CreateCompilationWithAnalyzersCacheEntryAsync(cancellationToken).ConfigureAwait(false); - } - - lock (s_gate) - { - if (s_compilationWithAnalyzersCache?.SolutionChecksum == _solutionChecksum && - s_compilationWithAnalyzersCache.Project == _project) - { - return s_compilationWithAnalyzersCache; - } - } - - var entry = await CreateCompilationWithAnalyzersCacheEntryAsync(cancellationToken).ConfigureAwait(false); - - lock (s_gate) - { - s_compilationWithAnalyzersCache = entry; - } - - return entry; - } - } - - private async Task CreateCompilationWithAnalyzersCacheEntryAsync(CancellationToken cancellationToken) - { - // We could consider creating a service so that we don't do this repeatedly if this shows up as perf cost - using var pooledObject = SharedPools.Default>().GetPooledObject(); - using var pooledMapProjectAnalyzerMap = SharedPools.Default>().GetPooledObject(); - using var pooledMapHostAnalyzerMap = SharedPools.Default>().GetPooledObject(); - var referenceSet = pooledObject.Object; - var projectAnalyzerMapBuilder = pooledMapProjectAnalyzerMap.Object; - var hostAnalyzerMapBuilder = pooledMapHostAnalyzerMap.Object; - - // This follows what we do in DiagnosticAnalyzerInfoCache.CheckAnalyzerReferenceIdentity - using var _1 = ArrayBuilder.GetInstance(out var projectAnalyzerBuilder); - using var _2 = ArrayBuilder.GetInstance(out var hostAnalyzerBuilder); - foreach (var reference in _project.Solution.AnalyzerReferences) - { - if (!referenceSet.Add(reference.Id)) - { - continue; - } - - var analyzers = reference.GetAnalyzers(_project.Language); - - // At times some Host analyzers should be treated as project analyzers and - // not be given access to the Host fallback options. In particular when we - // replace SDK CodeStyle analyzers with the Features analyzers. - if (ShouldRedirectAnalyzers(_project, reference)) - { - projectAnalyzerBuilder.AddRange(analyzers); - projectAnalyzerMapBuilder.AppendAnalyzerMap(analyzers); - } - else - { - hostAnalyzerBuilder.AddRange(analyzers); - hostAnalyzerMapBuilder.AppendAnalyzerMap(analyzers); - } - } - - // Clear the set -- we want these two loops to be independent - referenceSet.Clear(); - - foreach (var reference in _project.AnalyzerReferences) - { - if (!referenceSet.Add(reference.Id)) - { - continue; - } - - var analyzers = reference.GetAnalyzers(_project.Language); - projectAnalyzerBuilder.AddRange(analyzers); - - var projectSuppressors = analyzers.WhereAsArray(static a => a is DiagnosticSuppressor); - // Make sure to remove any project suppressors already in the host analyzer array so we don't end up with - // duplicates. - hostAnalyzerBuilder.RemoveRange(projectSuppressors); - hostAnalyzerBuilder.AddRange(projectSuppressors); - - projectAnalyzerMapBuilder.AppendAnalyzerMap(analyzers); - } - - var compilationWithAnalyzers = projectAnalyzerBuilder.Count > 0 || hostAnalyzerBuilder.Count > 0 - ? await CreateCompilationWithAnalyzerAsync(projectAnalyzerBuilder.ToImmutable(), hostAnalyzerBuilder.ToImmutable(), cancellationToken).ConfigureAwait(false) - : null; - var projectAnalyzerToIdMap = new BidirectionalMap(projectAnalyzerMapBuilder); - var hostAnalyzerToIdMap = new BidirectionalMap(hostAnalyzerMapBuilder); - - return new CompilationWithAnalyzersCacheEntry(_solutionChecksum, _project, compilationWithAnalyzers, projectAnalyzerToIdMap, hostAnalyzerToIdMap); - - static bool ShouldRedirectAnalyzers(Project project, AnalyzerReference reference) - { - // When replacing SDK CodeStyle analyzers we should redirect Features analyzers - // so they are treated as project analyzers. - return project.State.HasSdkCodeStyleAnalyzers && reference.IsFeaturesAnalyzer(); - } - } - - private async Task CreateCompilationWithAnalyzerAsync(ImmutableArray projectAnalyzers, ImmutableArray hostAnalyzers, CancellationToken cancellationToken) - { - Contract.ThrowIfFalse(!projectAnalyzers.IsEmpty || !hostAnalyzers.IsEmpty); - - // Always run analyzers concurrently in OOP - const bool concurrentAnalysis = true; - - // Get original compilation - var compilation = await _project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); - - // Fork compilation with concurrent build. this is okay since WithAnalyzers will fork compilation - // anyway to attach event queue. This should make compiling compilation concurrent and make things - // faster - compilation = compilation.WithOptions(compilation.Options.WithConcurrentBuild(concurrentAnalysis)); - - // Run analyzers concurrently, with performance logging and reporting suppressed diagnostics. - // This allows all client requests with or without performance data and/or suppressed diagnostics to be satisfied. - // TODO: can we support analyzerExceptionFilter in remote host? - // right now, host doesn't support watson, we might try to use new NonFatal watson API? - var projectAnalyzerOptions = new CompilationWithAnalyzersOptions( - options: _project.AnalyzerOptions, - onAnalyzerException: null, - analyzerExceptionFilter: null, - concurrentAnalysis: concurrentAnalysis, - logAnalyzerExecutionTime: true, - reportSuppressedDiagnostics: true); - var hostAnalyzerOptions = new CompilationWithAnalyzersOptions( - options: _project.HostAnalyzerOptions, - onAnalyzerException: null, - analyzerExceptionFilter: null, - concurrentAnalysis: concurrentAnalysis, - logAnalyzerExecutionTime: true, - reportSuppressedDiagnostics: true); - - return new CompilationWithAnalyzersPair( - projectAnalyzers.Any() ? compilation.WithAnalyzers(projectAnalyzers, projectAnalyzerOptions) : null, - hostAnalyzers.Any() ? compilation.WithAnalyzers(hostAnalyzers, hostAnalyzerOptions) : null); - } - - private sealed class CompilationWithAnalyzersCacheEntry - { - public Checksum SolutionChecksum { get; } - public Project Project { get; } - public CompilationWithAnalyzersPair? CompilationWithAnalyzers { get; } - public BidirectionalMap ProjectAnalyzerToIdMap { get; } - public BidirectionalMap HostAnalyzerToIdMap { get; } - - public CompilationWithAnalyzersCacheEntry( - Checksum solutionChecksum, - Project project, - CompilationWithAnalyzersPair? compilationWithAnalyzers, - BidirectionalMap projectAnalyzerToIdMap, - BidirectionalMap hostAnalyzerToIdMap) - { - SolutionChecksum = solutionChecksum; - Project = project; - CompilationWithAnalyzers = compilationWithAnalyzers; - ProjectAnalyzerToIdMap = projectAnalyzerToIdMap; - HostAnalyzerToIdMap = hostAnalyzerToIdMap; - } - } -} diff --git a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceQueue.cs b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceQueue.cs index 1aee6ea5aef..8a11b0ee918 100644 --- a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceQueue.cs +++ b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceQueue.cs @@ -186,7 +186,7 @@ private static void Reset( /// private sealed class AnalyzerNumberAssigner { - public static readonly AnalyzerNumberAssigner Instance = new AnalyzerNumberAssigner(); + public static readonly AnalyzerNumberAssigner Instance = new(); private int _currentId; diff --git a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceTrackerService.cs b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceTrackerService.cs index 5a22f206502..79639530182 100644 --- a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceTrackerService.cs +++ b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceTrackerService.cs @@ -37,7 +37,7 @@ internal sealed class PerformanceTrackerService : IPerformanceTrackerService private static readonly Func, int, bool, string> s_snapshotLogger = SnapshotLogger; private readonly PerformanceQueue _queueForDocumentAnalysis, _queueForSpanAnalysis; - private readonly ConcurrentDictionary _builtInMap = new ConcurrentDictionary(concurrencyLevel: 2, capacity: 10); + private readonly ConcurrentDictionary _builtInMap = new(concurrencyLevel: 2, capacity: 10); public event EventHandler SnapshotAdded; diff --git a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs index a4dc0cc9a35..f7a9c14a224 100644 --- a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs +++ b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Internal.Log; @@ -24,14 +25,43 @@ protected override IRemoteDiagnosticAnalyzerService CreateService(in ServiceCons => new RemoteDiagnosticAnalyzerService(arguments); } - public ValueTask> ProduceProjectDiagnosticsAsync( + public ValueTask> ForceRunCodeAnalysisDiagnosticsAsync( + Checksum solutionChecksum, ProjectId projectId, CancellationToken cancellationToken) + { + return RunWithSolutionAsync( + solutionChecksum, + async solution => + { + var project = solution.GetRequiredProject(projectId); + var service = solution.Services.GetRequiredService(); + return await service.ForceRunCodeAnalysisDiagnosticsAsync( + project, cancellationToken).ConfigureAwait(false); + }, + cancellationToken); + } + + public ValueTask IsAnyDiagnosticIdDeprioritizedAsync( + Checksum solutionChecksum, ProjectId projectId, ImmutableArray diagnosticIds, CancellationToken cancellationToken) + { + return RunWithSolutionAsync( + solutionChecksum, + async solution => + { + var project = solution.GetRequiredProject(projectId); + var service = solution.Services.GetRequiredService(); + + return await service.IsAnyDiagnosticIdDeprioritizedAsync( + project, diagnosticIds, cancellationToken).ConfigureAwait(false); + }, + cancellationToken); + } + + public ValueTask> GetDiagnosticsForIdsAsync( Checksum solutionChecksum, ProjectId projectId, - ImmutableHashSet analyzerIds, - ImmutableHashSet? diagnosticIds, ImmutableArray documentIds, + ImmutableHashSet? diagnosticIds, + AnalyzerFilter analyzerFilter, bool includeLocalDocumentDiagnostics, - bool includeNonLocalDocumentDiagnostics, - bool includeProjectNonLocalResult, CancellationToken cancellationToken) { return RunWithSolutionAsync( @@ -39,14 +69,30 @@ public ValueTask> ProduceProjectDiagnosticsAsync( async solution => { var project = solution.GetRequiredProject(projectId); - var service = (DiagnosticAnalyzerService)solution.Services.GetRequiredService(); + var service = solution.Services.GetRequiredService(); - var allProjectAnalyzers = service.GetProjectAnalyzers(project); + return await service.GetDiagnosticsForIdsAsync( + project, documentIds, diagnosticIds, analyzerFilter, + includeLocalDocumentDiagnostics, cancellationToken).ConfigureAwait(false); + }, + cancellationToken); + } - return await service.ProduceProjectDiagnosticsAsync( - project, allProjectAnalyzers.FilterAnalyzers(analyzerIds), diagnosticIds, documentIds, - includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics, includeProjectNonLocalResult, - cancellationToken).ConfigureAwait(false); + public ValueTask> GetProjectDiagnosticsForIdsAsync( + Checksum solutionChecksum, ProjectId projectId, + ImmutableHashSet? diagnosticIds, + AnalyzerFilter analyzerFilter, + CancellationToken cancellationToken) + { + return RunWithSolutionAsync( + solutionChecksum, + async solution => + { + var project = solution.GetRequiredProject(projectId); + var service = solution.Services.GetRequiredService(); + + return await service.GetProjectDiagnosticsForIdsAsync( + project, diagnosticIds, analyzerFilter, cancellationToken).ConfigureAwait(false); }, cancellationToken); } @@ -112,6 +158,20 @@ public ValueTask> GetDiagnosticDescript cancellationToken); } + public ValueTask> GetCompilationEndDiagnosticDescriptorIdsAsync( + Checksum solutionChecksum, CancellationToken cancellationToken) + { + return RunWithSolutionAsync( + solutionChecksum, + async solution => + { + var service = solution.Services.GetRequiredService(); + return await service.GetCompilationEndDiagnosticDescriptorIdsAsync( + solution, cancellationToken).ConfigureAwait(false); + }, + cancellationToken); + } + public ValueTask> GetDiagnosticDescriptorsAsync( Checksum solutionChecksum, ProjectId projectId, @@ -137,6 +197,7 @@ public ValueTask> GetDiagnosticDescript public ValueTask>> GetDiagnosticDescriptorsPerReferenceAsync( Checksum solutionChecksum, + ProjectId? projectId, CancellationToken cancellationToken) { return RunWithSolutionAsync( @@ -144,7 +205,7 @@ public ValueTask { var service = solution.Services.GetRequiredService(); - var map = await service.GetDiagnosticDescriptorsPerReferenceAsync(solution, cancellationToken).ConfigureAwait(false); + var map = await service.GetDiagnosticDescriptorsPerReferenceAsync(solution, projectId, cancellationToken).ConfigureAwait(false); return map.ToImmutableDictionary( kvp => kvp.Key, kvp => kvp.Value.SelectAsArray(DiagnosticDescriptorData.Create)); @@ -153,54 +214,13 @@ public ValueTask>> GetDiagnosticDescriptorsPerReferenceAsync( + public ValueTask> GetDiagnosticsForSpanAsync( Checksum solutionChecksum, - ProjectId projectId, - CancellationToken cancellationToken) - { - return RunWithSolutionAsync( - solutionChecksum, - async solution => - { - var project = solution.GetRequiredProject(projectId); - var service = solution.Services.GetRequiredService(); - var map = await service.GetDiagnosticDescriptorsPerReferenceAsync(project, cancellationToken).ConfigureAwait(false); - return map.ToImmutableDictionary( - kvp => kvp.Key, - kvp => kvp.Value.SelectAsArray(DiagnosticDescriptorData.Create)); - - }, - cancellationToken); - } - - public ValueTask> GetDeprioritizationCandidatesAsync( - Checksum solutionChecksum, ProjectId projectId, ImmutableHashSet analyzerIds, CancellationToken cancellationToken) - { - return RunWithSolutionAsync( - solutionChecksum, - async solution => - { - var project = solution.GetRequiredProject(projectId); - var service = (DiagnosticAnalyzerService)solution.Services.GetRequiredService(); - - var allProjectAnalyzers = service.GetProjectAnalyzers(project); - - var candidates = await service.GetDeprioritizationCandidatesAsync( - project, allProjectAnalyzers.FilterAnalyzers(analyzerIds), cancellationToken).ConfigureAwait(false); - - return candidates.Select(c => c.GetAnalyzerId()).ToImmutableHashSet(); - }, - cancellationToken); - } - - public ValueTask> ComputeDiagnosticsAsync( - Checksum solutionChecksum, DocumentId documentId, TextSpan? range, - ImmutableHashSet allAnalyzerIds, - ImmutableHashSet syntaxAnalyzersIds, - ImmutableHashSet semanticSpanAnalyzersIds, - ImmutableHashSet semanticDocumentAnalyzersIds, - bool incrementalAnalysis, - bool logPerformanceInfo, + DocumentId documentId, + TextSpan? range, + DiagnosticIdFilter diagnosticIdFilter, + CodeActionRequestPriority? priority, + DiagnosticKind diagnosticKind, CancellationToken cancellationToken) { return RunWithSolutionAsync( @@ -209,17 +229,10 @@ public ValueTask> ComputeDiagnosticsAsync( { var document = await solution.GetRequiredTextDocumentAsync( documentId, cancellationToken).ConfigureAwait(false); - var service = (DiagnosticAnalyzerService)solution.Services.GetRequiredService(); - - var allProjectAnalyzers = service.GetProjectAnalyzers(document.Project); + var service = solution.Services.GetRequiredService(); - return await service.ComputeDiagnosticsAsync( - document, range, - allProjectAnalyzers.FilterAnalyzers(allAnalyzerIds), - allProjectAnalyzers.FilterAnalyzers(syntaxAnalyzersIds), - allProjectAnalyzers.FilterAnalyzers(semanticSpanAnalyzersIds), - allProjectAnalyzers.FilterAnalyzers(semanticDocumentAnalyzersIds), - incrementalAnalysis, logPerformanceInfo, cancellationToken).ConfigureAwait(false); + return await service.GetDiagnosticsForSpanAsync( + document, range, diagnosticIdFilter, priority, diagnosticKind, cancellationToken).ConfigureAwait(false); }, cancellationToken); } diff --git a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/EditAndContinueLogReporter.cs b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/EditAndContinueLogReporter.cs index 700ec0765e2..ddb7f483655 100644 --- a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/EditAndContinueLogReporter.cs +++ b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/EditAndContinueLogReporter.cs @@ -19,6 +19,8 @@ namespace Microsoft.CodeAnalysis.EditAndContinue; [Export(typeof(IEditAndContinueLogReporter))] internal sealed class EditAndContinueLogReporter : IEditAndContinueLogReporter { + private const string CategoryName = "Roslyn"; + private readonly AsyncBatchingWorkQueue _queue; [ImportingConstructor] @@ -59,6 +61,6 @@ public void Report(string message, LogMessageSeverity severity) _ => throw ExceptionUtilities.UnexpectedValue(severity), }; - _queue.AddWork(new HotReloadLogMessage(verbosity, message, errorLevel: errorLevel)); + _queue.AddWork(new HotReloadLogMessage(verbosity, message, errorLevel: errorLevel, category: CategoryName)); } } diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs index e74bd19a931..79e72209131 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs @@ -176,6 +176,7 @@ public int Convert(TSyntaxKind kind) where TSyntaxKind : struct public int ElseClause => (int)SyntaxKind.ElseClause; public int EqualsValueClause => (int)SyntaxKind.EqualsValueClause; + public int? ExpressionElement => (int)SyntaxKind.ExpressionElement; public int? ImplicitElementAccess => (int)SyntaxKind.ImplicitElementAccess; public int Interpolation => (int)SyntaxKind.Interpolation; public int InterpolatedStringExpression => (int)SyntaxKind.InterpolatedStringExpression; diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems index 61cc4c1c411..71efa77a35e 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems @@ -543,7 +543,6 @@ - diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.DataFlowAnalyzer.FlowGraphAnalysisData.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.DataFlowAnalyzer.FlowGraphAnalysisData.cs index 97ab1349215..fa5d00eb49c 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.DataFlowAnalyzer.FlowGraphAnalysisData.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.DataFlowAnalyzer.FlowGraphAnalysisData.cs @@ -222,7 +222,7 @@ public BasicBlockAnalysisData GetOrCreateBlockAnalysisData(BasicBlock basicBlock // Filter down the operations to writes within this block range. writesInBlockRange = PooledHashSet<(ISymbol, IOperation)>.GetInstance(); - foreach (var (symbol, write) in SymbolsWriteBuilder.Where(kvp => !kvp.Value).Select(kvp => kvp.Key).ToArray()) + foreach (var (symbol, write) in SymbolsWriteBuilder.SelectAsArray(kvp => !kvp.Value, kvp => kvp.Key)) { if (write != null && operations.Contains(write)) { diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageResult.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageResult.cs index e0fcc56a680..44d79372c4f 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageResult.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageResult.cs @@ -44,8 +44,8 @@ public bool HasUnreadSymbolWrites() /// Gets symbol writes that have are never read. /// WriteOperation will be null for the initial value write to parameter symbols from the callsite. /// - public IEnumerable<(ISymbol Symbol, IOperation WriteOperation)> GetUnreadSymbolWrites() - => SymbolWritesMap.Where(kvp => !kvp.Value).Select(kvp => kvp.Key); + public ImmutableArray<(ISymbol Symbol, IOperation WriteOperation)> GetUnreadSymbolWrites() + => SymbolWritesMap.SelectAsArray(kvp => !kvp.Value, kvp => kvp.Key); /// /// Returns true if the initial value of the parameter from the caller is used. diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs index fbb47a46606..9c8859760a0 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs @@ -709,7 +709,7 @@ public static bool IsWhitespaceTrivia(this ISyntaxFacts syntaxFacts, SyntaxTrivi => trivia.RawKind == syntaxFacts.SyntaxKinds.WhitespaceTrivia; public static bool IsSkippedTokensTrivia(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.SkippedTokensTrivia; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.SkippedTokensTrivia; #endregion @@ -749,136 +749,136 @@ public static bool IsInterpolatedStringTextToken(this ISyntaxFacts syntaxFacts, #region names public static bool IsAliasQualifiedName(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.AliasQualifiedName; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.AliasQualifiedName; public static bool IsGenericName(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.GenericName; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.GenericName; public static bool IsIdentifierName(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.IdentifierName; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.IdentifierName; public static bool IsQualifiedName(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.QualifiedName; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.QualifiedName; #endregion #region types public static bool IsTupleType(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.TupleType; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.TupleType; #endregion #region literal expressions public static bool IsCharacterLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.CharacterLiteralExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.CharacterLiteralExpression; public static bool IsDefaultLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.DefaultLiteralExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.DefaultLiteralExpression; public static bool IsFalseLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.FalseLiteralExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.FalseLiteralExpression; public static bool IsNumericLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.NumericLiteralExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.NumericLiteralExpression; public static bool IsNullLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.NullLiteralExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.NullLiteralExpression; public static bool IsStringLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.StringLiteralExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.StringLiteralExpression; public static bool IsTrueLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.TrueLiteralExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.TrueLiteralExpression; #endregion #region expressions public static bool IsArrayCreationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ArrayCreationExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ArrayCreationExpression; public static bool IsAwaitExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.AwaitExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.AwaitExpression; public static bool IsBaseExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.BaseExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.BaseExpression; public static bool IsConditionalExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ConditionalExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ConditionalExpression; public static bool IsConditionalAccessExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ConditionalAccessExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ConditionalAccessExpression; public static bool IsFieldExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.FieldExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.FieldExpression; public static bool IsImplicitArrayCreationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ImplicitArrayCreationExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ImplicitArrayCreationExpression; public static bool IsImplicitObjectCreationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ImplicitObjectCreationExpression; public static bool IsIndexExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.IndexExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.IndexExpression; public static bool IsInterpolatedStringExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.InterpolatedStringExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.InterpolatedStringExpression; public static bool IsInterpolation(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.Interpolation; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.Interpolation; public static bool IsInterpolatedStringText(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.InterpolatedStringText; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.InterpolatedStringText; public static bool IsInvocationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.InvocationExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.InvocationExpression; public static bool IsIsTypeExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.IsTypeExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.IsTypeExpression; public static bool IsIsNotTypeExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.IsNotTypeExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.IsNotTypeExpression; public static bool IsIsPatternExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.IsPatternExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.IsPatternExpression; public static bool IsLogicalAndExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.LogicalAndExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.LogicalAndExpression; public static bool IsLogicalOrExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.LogicalOrExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.LogicalOrExpression; public static bool IsLogicalNotExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.LogicalNotExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.LogicalNotExpression; public static bool IsObjectCreationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ObjectCreationExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ObjectCreationExpression; public static bool IsParenthesizedExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ParenthesizedExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ParenthesizedExpression; public static bool IsQueryExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.QueryExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.QueryExpression; public static bool IsRangeExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.RangeExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.RangeExpression; public static bool IsRefExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.RefExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.RefExpression; public static bool IsSimpleMemberAccessExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.SimpleMemberAccessExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.SimpleMemberAccessExpression; public static bool IsThisExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ThisExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ThisExpression; public static bool IsThrowExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ThrowExpression; public static bool IsTupleExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.TupleExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.TupleExpression; public static bool ContainsGlobalStatement(this ISyntaxFacts syntaxFacts, SyntaxNode node) => node.ChildNodes().Any(c => c.RawKind == syntaxFacts.SyntaxKinds.GlobalStatement); @@ -888,142 +888,145 @@ public static bool ContainsGlobalStatement(this ISyntaxFacts syntaxFacts, Syntax #region pattern public static bool IsAndPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.AndPattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.AndPattern; public static bool IsConstantPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ConstantPattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ConstantPattern; public static bool IsDeclarationPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.DeclarationPattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.DeclarationPattern; public static bool IsListPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ListPattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ListPattern; public static bool IsNotPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.NotPattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.NotPattern; public static bool IsOrPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.OrPattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.OrPattern; public static bool IsParenthesizedPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ParenthesizedPattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ParenthesizedPattern; public static bool IsRecursivePattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.RecursivePattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.RecursivePattern; public static bool IsRelationalPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.RelationalPattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.RelationalPattern; public static bool IsTypePattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.TypePattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.TypePattern; public static bool IsVarPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.VarPattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.VarPattern; #endregion #region statements public static bool IsExpressionStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ExpressionStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ExpressionStatement; public static bool IsForEachStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ForEachStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ForEachStatement; public static bool IsForStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ForStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ForStatement; public static bool IsIfStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.IfStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.IfStatement; public static bool IsLocalDeclarationStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.LocalDeclarationStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.LocalDeclarationStatement; public static bool IsLocalFunctionStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) => node != null && node.RawKind == syntaxFacts.SyntaxKinds.LocalFunctionStatement; public static bool IsLockStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.LockStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.LockStatement; public static bool IsReturnStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ReturnStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ReturnStatement; public static bool IsThrowStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ThrowStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ThrowStatement; public static bool IsUsingStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.UsingStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.UsingStatement; public static bool IsWhileStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.WhileStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.WhileStatement; public static bool IsYieldReturnStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.YieldReturnStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.YieldReturnStatement; #endregion #region members/declarations public static bool IsAttribute(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.Attribute; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.Attribute; public static bool IsClassDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ClassDeclaration; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ClassDeclaration; public static bool IsConstructorDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ConstructorDeclaration; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ConstructorDeclaration; public static bool IsEnumDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.EnumDeclaration; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.EnumDeclaration; public static bool IsGlobalAttribute(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) => syntaxFacts.IsGlobalAssemblyAttribute(node) || syntaxFacts.IsGlobalModuleAttribute(node); public static bool IsInterfaceDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.InterfaceDeclaration; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.InterfaceDeclaration; public static bool IsParameter(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.Parameter; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.Parameter; public static bool IsTypeConstraint(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.TypeConstraint; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.TypeConstraint; public static bool IsVariableDeclarator(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.VariableDeclarator; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.VariableDeclarator; public static bool IsFieldDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.FieldDeclaration; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.FieldDeclaration; public static bool IsPropertyDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.PropertyDeclaration; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.PropertyDeclaration; public static bool IsStructDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.StructDeclaration; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.StructDeclaration; public static bool IsTypeArgumentList(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.TypeArgumentList; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.TypeArgumentList; #endregion #region clauses public static bool IsElseClause(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ElseClause; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ElseClause; public static bool IsEqualsValueClause(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.EqualsValueClause; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.EqualsValueClause; #endregion #region other + public static bool IsExpressionElement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ExpressionElement; + public static bool IsImplicitElementAccess(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ImplicitElementAccess; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ImplicitElementAccess; public static bool IsIndexerMemberCref(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.IndexerMemberCref; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.IndexerMemberCref; public static bool IsPrimaryConstructorBaseType(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.PrimaryConstructorBaseType; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.PrimaryConstructorBaseType; #endregion diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs index 41981d1cb8d..ac40972113d 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs @@ -233,6 +233,7 @@ internal interface ISyntaxKinds #region other + int? ExpressionElement { get; } int? ImplicitElementAccess { get; } int Interpolation { get; } int InterpolatedStringText { get; } diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/EditDistance.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/EditDistance.cs index 1a54b4bb267..f0c8287b169 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/EditDistance.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/EditDistance.cs @@ -50,7 +50,7 @@ internal readonly struct EditDistance(string text) : IDisposable private readonly char[] _sourceLowerCaseCharacters = ConvertToLowercaseArray(text); private const int PooledArraySize = 512; - private static readonly ObjectPool s_pool = new ObjectPool(() => new char[PooledArraySize]); + private static readonly ObjectPool s_pool = new(() => new char[PooledArraySize]); private static char[] ConvertToLowercaseArray(string text) { diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Compilation/CompilationExtensions.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Compilation/CompilationExtensions.cs index 4fd2ef3ea2e..e04b8e5ba17 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Compilation/CompilationExtensions.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Compilation/CompilationExtensions.cs @@ -123,6 +123,9 @@ public static ImmutableArray GetReferencedAssemblySymbols(this public static INamedTypeSymbol? FormattableStringType(this Compilation compilation) => compilation.GetTypeByMetadataName(typeof(FormattableString).FullName!); + public static INamedTypeSymbol? IFormattableType(this Compilation compilation) + => compilation.GetTypeByMetadataName(typeof(IFormattable).FullName!); + public static INamedTypeSymbol? EventArgsType(this Compilation compilation) => compilation.GetTypeByMetadataName(typeof(EventArgs).FullName!); diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/ISymbolExtensions.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/ISymbolExtensions.cs index 04f773413ad..cae74193470 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/ISymbolExtensions.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/ISymbolExtensions.cs @@ -11,7 +11,6 @@ using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Runtime; using System.Runtime.CompilerServices; using System.Threading; using Microsoft.CodeAnalysis.PooledObjects; @@ -841,4 +840,35 @@ public static bool HasAttribute([NotNullWhen(true)] this ISymbol? symbol, [NotNu return symbol.GetAttributes().Any(static (attribute, attributeClass) => attributeClass.Equals(attribute.AttributeClass), attributeClass); } + + public static bool IsClassicOrModernInstanceExtensionMethod( + [NotNullWhen(true)] this ISymbol? symbol) + { + return IsClassicOrModernInstanceExtensionMethod(symbol, out _); + } + + public static bool IsClassicOrModernInstanceExtensionMethod( + [NotNullWhen(true)] this ISymbol? symbol, + [NotNullWhen(true)] out IMethodSymbol? classicExtensionMethod) + { + if (symbol is IMethodSymbol method) + { + if (method.IsExtensionMethod) + { + classicExtensionMethod = method; + return true; + } + +#if !ROSLYN_4_12_OR_LOWER + if (method is { IsStatic: false, AssociatedExtensionImplementation: { } associatedMethod }) + { + classicExtensionMethod = associatedMethod; + return true; + } +#endif + } + + classicExtensionMethod = null; + return false; + } } diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb index 104f2d01b47..26a8a3cd93a 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb @@ -180,6 +180,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Public ReadOnly Property ElseClause As Integer = SyntaxKind.ElseBlock Implements ISyntaxKinds.ElseClause Public ReadOnly Property EqualsValueClause As Integer = SyntaxKind.EqualsValue Implements ISyntaxKinds.EqualsValueClause + Public ReadOnly Property ExpressionElement As Integer? Implements ISyntaxKinds.ExpressionElement Public ReadOnly Property ImplicitElementAccess As Integer? Implements ISyntaxKinds.ImplicitElementAccess Public ReadOnly Property Interpolation As Integer = SyntaxKind.Interpolation Implements ISyntaxKinds.Interpolation Public ReadOnly Property InterpolatedStringExpression As Integer = SyntaxKind.InterpolatedStringExpression Implements ISyntaxKinds.InterpolatedStringExpression diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/SyntaxEditorBasedCodeFixProvider.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/SyntaxEditorBasedCodeFixProvider.cs index 53142408658..f14bdbbadb4 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/SyntaxEditorBasedCodeFixProvider.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/SyntaxEditorBasedCodeFixProvider.cs @@ -102,10 +102,9 @@ protected abstract Task FixAllAsync( /// create multiple diagnostics for the same issue (For example, one main diagnostic and multiple 'faded out code' /// diagnostics). FixAll can be invoked from any of those, but we'll only want perform an edit for only one /// diagnostic for each of those sets of diagnostics. - /// - /// This overload differs from in that it also passes along - /// the in case that would be useful (for example if the is used. + /// This overload differs from in that it also + /// passes along the in case that would be useful (for example if the + /// CodeActionEquivalenceKey is used). /// /// Only one of these two overloads needs to be overridden if you want to customize behavior. /// @@ -117,12 +116,11 @@ protected virtual bool IncludeDiagnosticDuringFixAll(Diagnostic diagnostic, Docu /// create multiple diagnostics for the same issue (For example, one main diagnostic and multiple 'faded out code' /// diagnostics). FixAll can be invoked from any of those, but we'll only want perform an edit for only one /// diagnostic for each of those sets of diagnostics. - /// - /// By default, all diagnostics will be included in fix-all unless they are filtered out here. If only the + /// By default, all diagnostics will be included in fix-all unless they are filtered out here. If only the /// diagnostic needs to be queried to make this determination, only this overload needs to be overridden. However, - /// if information from is needed (for example ), then should be overridden instead. + /// if information from is needed (for example the CodeActionEquivalenceKey), then should be overridden + /// instead. /// /// Only one of these two overloads needs to be overridden if you want to customize behavior. /// diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixesAndRefactorings/IFixAllSpanMappingService.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixesAndRefactorings/IFixAllSpanMappingService.cs index 82fff702ad1..4a0dc3dfb2a 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixesAndRefactorings/IFixAllSpanMappingService.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixesAndRefactorings/IFixAllSpanMappingService.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; /// -/// Language service for mapping spans for specific s for fix all occurences code fix. +/// Language service for mapping spans for specific s for fix all occurrences code fix. /// Every language that wants to support span based FixAll scopes, such as , /// , should implement this language service. Non-span based FixAll scopes, /// such as , and diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/CodeGenerator.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/CodeGenerator.cs index 3325f4796a1..b244cc32132 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/CodeGenerator.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/CodeGenerator.cs @@ -1,6 +1,7 @@ // 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.Threading; using System.Threading.Tasks; @@ -19,75 +20,75 @@ internal static class CodeGenerator /// public static readonly SyntaxAnnotation Annotation = new(nameof(CodeGenerator)); - private static ICodeGenerationService GetCodeGenerationService(HostWorkspaceServices services, string language) - => services.GetExtendedLanguageServices(language).GetRequiredService(); + private static ICodeGenerationService GetCodeGenerationService(Solution solution, string language) + => solution.GetExtendedLanguageServices(language).GetRequiredService(); /// /// Create a new solution where the declaration of the destination symbol has an additional event of the same signature as the specified event symbol. /// Returns the document in the new solution where the destination symbol is declared. /// public static Task AddEventDeclarationAsync(CodeGenerationSolutionContext context, INamedTypeSymbol destination, IEventSymbol @event, CancellationToken cancellationToken) - => GetCodeGenerationService(context.Solution.Workspace.Services, destination.Language).AddEventAsync(context, destination, @event, cancellationToken); + => GetCodeGenerationService(context.Solution, destination.Language).AddEventAsync(context, destination, @event, cancellationToken); /// /// Create a new solution where the declaration of the destination symbol has an additional field of the same signature as the specified field symbol. /// Returns the document in the new solution where the destination symbol is declared. /// public static Task AddFieldDeclarationAsync(CodeGenerationSolutionContext context, INamedTypeSymbol destination, IFieldSymbol field, CancellationToken cancellationToken) - => GetCodeGenerationService(context.Solution.Workspace.Services, destination.Language).AddFieldAsync(context, destination, field, cancellationToken); + => GetCodeGenerationService(context.Solution, destination.Language).AddFieldAsync(context, destination, field, cancellationToken); /// /// Create a new solution where the declaration of the destination symbol has an additional method of the same signature as the specified method symbol. /// Returns the document in the new solution where the destination symbol is declared. /// public static Task AddMethodDeclarationAsync(CodeGenerationSolutionContext context, INamedTypeSymbol destination, IMethodSymbol method, CancellationToken cancellationToken) - => GetCodeGenerationService(context.Solution.Workspace.Services, destination.Language).AddMethodAsync(context, destination, method, cancellationToken); + => GetCodeGenerationService(context.Solution, destination.Language).AddMethodAsync(context, destination, method, cancellationToken); /// /// Create a new solution where the declaration of the destination symbol has an additional property of the same signature as the specified property symbol. /// Returns the document in the new solution where the destination symbol is declared. /// public static Task AddPropertyDeclarationAsync(CodeGenerationSolutionContext context, INamedTypeSymbol destination, IPropertySymbol property, CancellationToken cancellationToken) - => GetCodeGenerationService(context.Solution.Workspace.Services, destination.Language).AddPropertyAsync(context, destination, property, cancellationToken); + => GetCodeGenerationService(context.Solution, destination.Language).AddPropertyAsync(context, destination, property, cancellationToken); /// /// Create a new solution where the declaration of the destination symbol has an additional named type of the same signature as the specified named type symbol. /// Returns the document in the new solution where the destination symbol is declared. /// public static Task AddNamedTypeDeclarationAsync(CodeGenerationSolutionContext context, INamedTypeSymbol destination, INamedTypeSymbol namedType, CancellationToken cancellationToken) - => GetCodeGenerationService(context.Solution.Workspace.Services, destination.Language).AddNamedTypeAsync(context, destination, namedType, cancellationToken); + => GetCodeGenerationService(context.Solution, destination.Language).AddNamedTypeAsync(context, destination, namedType, cancellationToken); /// /// Create a new solution where the declaration of the destination symbol has an additional named type of the same signature as the specified named type symbol. /// Returns the document in the new solution where the destination symbol is declared. /// public static Task AddNamedTypeDeclarationAsync(CodeGenerationSolutionContext context, INamespaceSymbol destination, INamedTypeSymbol namedType, CancellationToken cancellationToken) - => GetCodeGenerationService(context.Solution.Workspace.Services, destination.Language).AddNamedTypeAsync(context, destination, namedType, cancellationToken); + => GetCodeGenerationService(context.Solution, destination.Language).AddNamedTypeAsync(context, destination, namedType, cancellationToken); /// /// Create a new solution where the declaration of the destination symbol has an additional namespace of the same signature as the specified namespace symbol. /// Returns the document in the new solution where the destination symbol is declared. /// public static Task AddNamespaceDeclarationAsync(CodeGenerationSolutionContext context, INamespaceSymbol destination, INamespaceSymbol @namespace, CancellationToken cancellationToken) - => GetCodeGenerationService(context.Solution.Workspace.Services, destination.Language).AddNamespaceAsync(context, destination, @namespace, cancellationToken); + => GetCodeGenerationService(context.Solution, destination.Language).AddNamespaceAsync(context, destination, @namespace, cancellationToken); /// /// Create a new solution where the declaration of the destination symbol has an additional namespace or type of the same signature as the specified namespace or type symbol. /// Returns the document in the new solution where the destination symbol is declared. /// public static Task AddNamespaceOrTypeDeclarationAsync(CodeGenerationSolutionContext context, INamespaceSymbol destination, INamespaceOrTypeSymbol namespaceOrType, CancellationToken cancellationToken) - => GetCodeGenerationService(context.Solution.Workspace.Services, destination.Language).AddNamespaceOrTypeAsync(context, destination, namespaceOrType, cancellationToken); + => GetCodeGenerationService(context.Solution, destination.Language).AddNamespaceOrTypeAsync(context, destination, namespaceOrType, cancellationToken); /// /// Create a new solution where the declaration of the destination symbol has additional members of the same signature as the specified member symbols. /// Returns the document in the new solution where the destination symbol is declared. /// public static Task AddMemberDeclarationsAsync(CodeGenerationSolutionContext context, INamedTypeSymbol destination, IEnumerable members, CancellationToken cancellationToken) - => GetCodeGenerationService(context.Solution.Workspace.Services, destination.Language).AddMembersAsync(context, destination, members, cancellationToken); + => GetCodeGenerationService(context.Solution, destination.Language).AddMembersAsync(context, destination, members, cancellationToken); /// /// Returns true if additional declarations can be added to the destination symbol's declaration. /// public static bool CanAdd(Solution solution, ISymbol destination, CancellationToken cancellationToken) - => GetCodeGenerationService(solution.Workspace.Services, destination.Language).CanAddTo(destination, solution, cancellationToken); + => GetCodeGenerationService(solution, destination.Language).CanAddTo(destination, solution, cancellationToken); } diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationAbstractMethodSymbol.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationAbstractMethodSymbol.cs index 57c8c7b7210..db6df0f49a5 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationAbstractMethodSymbol.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationAbstractMethodSymbol.cs @@ -101,6 +101,8 @@ public virtual ImmutableArray ReturnTypeCustomModifiers #if !ROSLYN_4_12_OR_LOWER public bool IsIterator => false; + + public IMethodSymbol AssociatedExtensionImplementation => null; #endif public SignatureCallingConvention CallingConvention => SignatureCallingConvention.Default; diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationAbstractNamedTypeSymbol.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationAbstractNamedTypeSymbol.cs index e695ebff662..03dd4825924 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationAbstractNamedTypeSymbol.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationAbstractNamedTypeSymbol.cs @@ -123,4 +123,10 @@ public override string MetadataName public bool IsSerializable => false; public bool IsFileLocal => Modifiers.IsFile; + +#if !ROSLYN_4_12_OR_LOWER + public bool IsExtension => false; + + public IParameterSymbol ExtensionParameter => null; +#endif } diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs index 52f4359e86f..6e102835155 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs @@ -43,9 +43,9 @@ public ImmutableArray AllInterfaces public bool IsNativeIntegerType => false; #if !ROSLYN_4_12_OR_LOWER - public bool IsExtension => false; + bool ITypeSymbol.IsExtension => false; - public IParameterSymbol ExtensionParameter => null; + IParameterSymbol ITypeSymbol.ExtensionParameter => null; #endif public static ImmutableArray TupleElementTypes => default; diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs index 322c15e27c2..63f9c0e132c 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs @@ -157,4 +157,24 @@ public static Solution WithUpToDateSourceGeneratorDocuments(this Solution soluti return null; } } + + public static TLanguageService? GetLanguageService(this Solution? solution, string languageName) where TLanguageService : ILanguageService + => solution is null ? default : solution.GetExtendedLanguageServices(languageName).GetService(); + + public static TLanguageService GetRequiredLanguageService(this Solution solution, string languageName) where TLanguageService : ILanguageService + => solution.GetExtendedLanguageServices(languageName).GetRequiredService(); + +#pragma warning disable RS0030 // Do not used banned API 'Project.LanguageServices', use 'GetExtendedLanguageServices' instead - allow in this helper. + + /// + /// Gets extended host language services, which includes language services from . + /// + public static HostLanguageServices GetExtendedLanguageServices(this Solution solution, string languageName) +#if !WORKSPACE + => solution.Workspace.Services.GetExtendedLanguageServices(languageName); +#else + => solution.Services.GetExtendedLanguageServices(languageName); +#endif + +#pragma warning restore RS0030 // Do not used banned APIs } diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ProjectExtensions.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ProjectExtensions.cs index 139ac3336f7..e3626a4af5f 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ProjectExtensions.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ProjectExtensions.cs @@ -15,31 +15,16 @@ namespace Microsoft.CodeAnalysis.Shared.Extensions; internal static partial class ProjectExtensions { public static TLanguageService? GetLanguageService(this Project? project) where TLanguageService : class, ILanguageService -#if CODE_STYLE - => project?.GetExtendedLanguageServices().GetService(); -#else - => project?.Services.GetService(); -#endif + => project?.Solution.GetLanguageService(project.Language); public static TLanguageService GetRequiredLanguageService(this Project project) where TLanguageService : class, ILanguageService -#if CODE_STYLE - => project.GetExtendedLanguageServices().GetRequiredService(); -#else - => project.Services.GetRequiredService(); -#endif + => project.Solution.GetRequiredLanguageService(project.Language); -#pragma warning disable RS0030 // Do not used banned API 'Project.LanguageServices', use 'GetExtendedLanguageServices' instead - allow in this helper. /// - /// Gets extended host language services, which includes language services from . + /// Gets extended host language services, which includes language services from . /// public static HostLanguageServices GetExtendedLanguageServices(this Project project) -#if !WORKSPACE - => project.Solution.Workspace.Services.GetExtendedLanguageServices(project.Language); -#else - => project.Solution.Services.GetExtendedLanguageServices(project.Language); -#endif - -#pragma warning restore RS0030 // Do not used banned APIs + => project.Solution.GetExtendedLanguageServices(project.Language); public static string? TryGetAnalyzerConfigPathForProjectConfiguration(this Project project) => TryGetAnalyzerConfigPathForProjectOrDiagnosticConfiguration(project, diagnostic: null); diff --git a/src/roslyn/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb b/src/roslyn/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb index e778643388c..bf1e8b991c7 100644 --- a/src/roslyn/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb +++ b/src/roslyn/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb @@ -3637,6 +3637,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Friend Overrides Function RemoveCommentLines(syntaxList As SyntaxTriviaList) As SyntaxTriviaList Return syntaxList.Where(Function(s) Not IsRegularOrDocComment(s)).ToSyntaxTriviaList() End Function + + Friend Overrides Function ExtensionBlockDeclaration(extensionParameter As SyntaxNode, typeParameters As IEnumerable(Of SyntaxNode), members As IEnumerable(Of SyntaxNode)) As SyntaxNode + Throw New NotSupportedException("Extension blocks are not supported in Visual Basic") + End Function + #End Region End Class diff --git a/src/roslyn/src/Workspaces/VisualBasicTest/CodeGeneration/SyntaxGeneratorTests.vb b/src/roslyn/src/Workspaces/VisualBasicTest/CodeGeneration/SyntaxGeneratorTests.vb index 396e593be60..3172e015174 100644 --- a/src/roslyn/src/Workspaces/VisualBasicTest/CodeGeneration/SyntaxGeneratorTests.vb +++ b/src/roslyn/src/Workspaces/VisualBasicTest/CodeGeneration/SyntaxGeneratorTests.vb @@ -4,6 +4,7 @@ Imports System.Globalization Imports System.Runtime.InteropServices +Imports Microsoft.CodeAnalysis.CSharp Imports Microsoft.CodeAnalysis.Editing Imports Microsoft.CodeAnalysis.Shared.Extensions Imports Microsoft.CodeAnalysis.Test.Utilities @@ -2475,6 +2476,22 @@ End Class")) "Public Const MaxValue As System.UInt32 = 4294967295UI") End Sub + + Public Sub TestExtensionBlock_01() + Dim code = " +static class E +{ + extension(int) + { + public void M() { } + } +} +" + Dim compilation = CSharpCompilation.Create("test").AddReferences(NetFramework.mscorlib).AddSyntaxTrees(CSharp.SyntaxFactory.ParseSyntaxTree(code)) + Dim e = compilation.GlobalNamespace.GetTypeMembers("E").Single() + Assert.Throws(Of NotSupportedException)(Sub() Generator.Declaration(e)) + End Sub + #End Region #Region "Add/Insert/Remove/Get/Set members & elements" diff --git a/src/source-manifest.json b/src/source-manifest.json index 70a1b9c7245..02c23b4ba37 100644 --- a/src/source-manifest.json +++ b/src/source-manifest.json @@ -73,10 +73,10 @@ "commitSha": "bde4f70c9560811a7f25023b9d8ac42fd7d0e99f" }, { - "barId": 281892, + "barId": 283091, "path": "roslyn", "remoteUri": "https://github.com/dotnet/roslyn", - "commitSha": "a70b5c16d07b7cba6c4955034a0c5957c86eb6e5" + "commitSha": "6ea0a05e20748e980ebc95de2a5584c35f490534" }, { "barId": 283055, From 6d6d031308fbd9b18147567e562d26560f0b311a Mon Sep 17 00:00:00 2001 From: Matt Thalman Date: Mon, 15 Sep 2025 11:22:38 -0500 Subject: [PATCH 4/6] Download shared components outside of prep script (#2403) --- eng/download-source-built-archive.sh | 108 ++++++++++++++++++ eng/pipelines/templates/jobs/vmr-build.yml | 48 +++----- .../stages/source-build-and-validate.yml | 8 ++ prep-source-build.sh | 107 +---------------- 4 files changed, 133 insertions(+), 138 deletions(-) create mode 100755 eng/download-source-built-archive.sh diff --git a/eng/download-source-built-archive.sh b/eng/download-source-built-archive.sh new file mode 100755 index 00000000000..1d4a13e1901 --- /dev/null +++ b/eng/download-source-built-archive.sh @@ -0,0 +1,108 @@ +#!/bin/bash + +# Common helper functions for downloading archives based on versions in XML files + +# Get the repository root directory +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +# Define the path to Versions.props once +PACKAGE_VERSIONS_PATH="$REPO_ROOT/eng/Versions.props" + +# Helper to extract a property value from an XML file +function GetXmlPropertyValue { + local propName="$1" + local filePath="$2" + local value="" + local line pattern + line=$(grep -m 1 "<$propName>" "$filePath" || :) + pattern="<$propName>(.*)" + if [[ $line =~ $pattern ]]; then + value="${BASH_REMATCH[1]}" + fi + echo "$value" +} + +# Helper to download a file with retries +function DownloadWithRetries { + local url="$1" + local targetDir="$2" + ( + cd "$targetDir" && + for i in {1..5}; do + if curl -fL --retry 5 -O "$url"; then + return 0 + else + case $? in + 18) + sleep 3 + ;; + *) + return 1 + ;; + esac + fi + done + return 1 + ) +} + +# Generic function to download an archive based on property name +function DownloadArchive { + local label="$1" + local propertyName="$2" + local isRequired="$3" + local artifactsRid="$4" + local outputDir="$5" + local destinationFilenamePrefix="${6:-}" + + local notFoundMessage="No $label found to download..." + + local archiveVersion + archiveVersion=$(GetXmlPropertyValue "$propertyName" "$PACKAGE_VERSIONS_PATH") + if [[ -z "$archiveVersion" ]]; then + if [ "$isRequired" == true ]; then + echo " ERROR: $notFoundMessage" + return 1 + else + echo " $notFoundMessage" + return 0 + fi + fi + + local archiveUrl + local artifactsBaseFileName="Private.SourceBuilt.Artifacts" + local prebuiltsBaseFileName="Private.SourceBuilt.Prebuilts" + local defaultArtifactsRid='centos.10-x64' + + if [[ "$propertyName" == "MicrosoftNETSdkVersion" ]]; then + archiveUrl="https://ci.dot.net/public/source-build/$artifactsBaseFileName.$archiveVersion.$artifactsRid.tar.gz" + elif [[ "$propertyName" == *Prebuilts* ]]; then + archiveUrl="https://builds.dotnet.microsoft.com/source-built-artifacts/assets/$prebuiltsBaseFileName.$archiveVersion.$defaultArtifactsRid.tar.gz" + elif [[ "$propertyName" == *Artifacts* ]]; then + archiveUrl="https://builds.dotnet.microsoft.com/source-built-artifacts/assets/$artifactsBaseFileName.$archiveVersion.$artifactsRid.tar.gz" + elif [[ "$propertyName" == *Sdk* ]]; then + archiveUrl="https://builds.dotnet.microsoft.com/source-built-artifacts/sdks/dotnet-sdk-$archiveVersion-$artifactsRid.tar.gz" + else + echo " ERROR: Unknown archive property name: $propertyName" + return 1 + fi + + echo " Downloading $label from $archiveUrl..." + if ! DownloadWithRetries "$archiveUrl" "$outputDir"; then + echo " ERROR: Failed to download $archiveUrl" + return 1 + fi + + # Rename the file if a destination filename prefix is provided + if [[ -n "$destinationFilenamePrefix" ]]; then + local downloadedFilename + downloadedFilename=$(basename "$archiveUrl") + # Extract the suffix from the downloaded filename + local suffix="${downloadedFilename#$artifactsBaseFileName}" + local newFilename="$destinationFilenamePrefix$suffix" + mv "$outputDir/$downloadedFilename" "$outputDir/$newFilename" + echo " Renamed $downloadedFilename to $newFilename" + fi + + return 0 +} diff --git a/eng/pipelines/templates/jobs/vmr-build.yml b/eng/pipelines/templates/jobs/vmr-build.yml index 1f2f91f1052..24a68706b11 100644 --- a/eng/pipelines/templates/jobs/vmr-build.yml +++ b/eng/pipelines/templates/jobs/vmr-build.yml @@ -79,6 +79,11 @@ parameters: type: boolean default: false +# Indicates whether to runtime dependent jobs are excluded in the build (this occurs for non-1xx branches). +- name: excludeRuntimeDependentJobs + type: boolean + default: false + #### SOURCE-ONLY parameters #### # Instead of building the VMR directly, exports the sources into a tarball and builds from that @@ -402,32 +407,15 @@ jobs: - ${{ if eq(parameters.withPreviousSDK, 'true') }}: - script: | - set -euo pipefail - - if [[ '${{ parameters.artifactsRid }}' == '' ]]; then - echo "'artifactsRid' is not specified. Cannot download source-built SDK." - exit 1 - fi - - packageVersionsPath="$(sourcesPath)/eng/Versions.props" - notFoundMessage="No source-built SDK found to download..." + source "$(sourcesPath)/eng/download-source-built-archive.sh" + DownloadArchive "source-built SDK" "PrivateSourceBuiltSdkVersion" false "${{ parameters.artifactsRid }}" "$(sourcesPath)/prereqs/packages/archive/" + displayName: Download Previously Source-Built SDK - echo "Looking for source-built SDK to download..." - archiveVersionLine=$(grep -m 1 "" "$packageVersionsPath" || :) - versionPattern="(.*)" - - if [[ $archiveVersionLine =~ $versionPattern ]]; then - archiveVersion="${BASH_REMATCH[1]}" - archiveUrl="https://builds.dotnet.microsoft.com/source-built-artifacts/sdks/dotnet-sdk-$archiveVersion-${{ parameters.artifactsRid }}.tar.gz" - downloadDir="$(sourcesPath)/prereqs/packages/archive/" - - echo "Downloading source-built SDK from $archiveUrl..." - (cd "$downloadDir" && curl --retry 5 -O "$archiveUrl") - else - echo "$notFoundMessage" - exit 1 - fi - displayName: Setup Previously Source-Built SDK + - ${{ if eq(parameters.excludeRuntimeDependentJobs, 'true') }}: + - script: | + source "$(sourcesPath)/eng/download-source-built-archive.sh" + DownloadArchive "shared component artifacts" "MicrosoftNETSdkVersion" false "${{ parameters.artifactsRid }}" "$(sourcesPath)/prereqs/packages/archive/" "Private.SourceBuilt.SharedComponents" + displayName: Download Shared Component Artifacts # https://github.com/dotnet/dotnet/issues/1758 # After a re-boostrap: @@ -516,20 +504,12 @@ jobs: customPrepArgs="" prepSdk=true - if [[ -n '${{ parameters.artifactsRid }}' ]]; then - customPrepArgs="${customPrepArgs} --artifacts-rid ${{ parameters.artifactsRid }}" - fi - if [[ '${{ parameters.withPreviousSDK }}' == 'True' ]]; then # Using previous SDK implies we do not want to bootstrap. customPrepArgs="${customPrepArgs} --no-sdk --no-bootstrap" prepSdk=false elif [[ '${{ length(parameters.reuseBuildArtifactsFrom) }}' -gt '0' ]]; then - # Don't use --no-artifacts because it will prevent the shared components archive from being - # downloaded in non-1xx branches. It's ok to not provide this option since the downloading - # of the PSB artifacts will be skipped anyway because we've already manually copied the file - # to use at the expected location. - customPrepArgs="${customPrepArgs} --no-sdk" + customPrepArgs="${customPrepArgs} --no-sdk --no-artifacts" prepSdk=false fi diff --git a/eng/pipelines/templates/stages/source-build-and-validate.yml b/eng/pipelines/templates/stages/source-build-and-validate.yml index d9b54e547a2..c630c68e161 100644 --- a/eng/pipelines/templates/stages/source-build-and-validate.yml +++ b/eng/pipelines/templates/stages/source-build-and-validate.yml @@ -133,6 +133,7 @@ stages: ${{ if eq(leg.disableSigning, false) }}: sign: ${{ variables.signEnabled }} signType: ${{ variables.signType }} + excludeRuntimeDependentJobs: ${{ parameters.excludeRuntimeDependentJobs }} # We only want to publish SB artifacts for each distinct distro/version/arch combination ${{ if and(endsWith(leg.buildNameFormat, 'Offline_MsftSdk'), not(contains(leg.buildNameFormat, '_Mono_'))) }}: @@ -161,12 +162,14 @@ stages: container: name: ${{ variables.centOSStreamContainerName }} image: ${{ variables.centOSStreamContainerImage }} + artifactsRid: ${{ variables.centOSStreamX64Rid }} # Fedora ${{ elseif eq(leg.distro, 'fedora') }}: buildName: ${{ format(leg.buildNameFormat, variables.fedoraName) }}_Validation container: name: ${{ variables.fedoraContainerName }} image: ${{ variables.fedoraContainerImage }} + artifactsRid: ${{ variables.fedoraX64Rid }} # Ubuntu ${{ elseif eq(leg.distro, 'ubuntu') }}: buildName: ${{ format(leg.buildNameFormat, variables.ubuntuName) }}_Validation @@ -174,16 +177,19 @@ stages: container: name: ${{ variables.ubuntuArmContainerName }} image: ${{ variables.ubuntuArmContainerImage }} + artifactsRid: ${{ variables.ubuntuArm64Rid }} ${{ else }}: container: name: ${{ variables.ubuntuContainerName }} image: ${{ variables.ubuntuContainerImage }} + artifactsRid: ${{ variables.ubuntuX64Rid }} # AlmaLinux ${{ elseif eq(leg.distro, 'almalinux') }}: buildName: ${{ format(leg.buildNameFormat, variables.almaLinuxName) }}_Validation container: name: ${{ variables.almaLinuxContainerName }} image: ${{ variables.almaLinuxContainerImage }} + artifactsRid: ${{ variables.almaLinuxX64Rid }} targetRid: ${{ variables.almaLinuxX64Rid }} # Alpine ${{ elseif eq(leg.distro, 'alpine') }}: @@ -191,6 +197,7 @@ stages: container: name: ${{ variables.alpineContainerName }} image: ${{ variables.alpineContainerImage }} + artifactsRid: ${{ variables.alpineX64Rid }} targetRid: ${{ variables.alpineX64Rid }} targetArchitecture: ${{ leg.targetArchitecture }} @@ -209,6 +216,7 @@ stages: /p:DotNetSourceOnlyTestOnly=true /p:ExtraRestoreSourcePath=$(Pipeline.Workspace)/msft-pkgs /p:RestoreAdditionalProjectSources=$(Pipeline.Workspace)/msft-pkgs + excludeRuntimeDependentJobs: ${{ parameters.excludeRuntimeDependentJobs }} testInitSteps: # Ensure the artifacts staging directory exists so that even if no files get placed there, it won't fail diff --git a/prep-source-build.sh b/prep-source-build.sh index 5e643479e6e..45d464b34b6 100755 --- a/prep-source-build.sh +++ b/prep-source-build.sh @@ -32,6 +32,9 @@ IFS=$'\n\t' source="${BASH_SOURCE[0]}" REPO_ROOT="$( cd -P "$( dirname "$0" )" && pwd )" +# Load common helper functions +source "$REPO_ROOT/eng/download-source-built-archive.sh" + function print_help () { sed -n '/^### /,/^$/p' "$source" | cut -b 5- } @@ -63,9 +66,6 @@ psbDir=$defaultPsbDir artifactsBaseFileName="Private.SourceBuilt.Artifacts" artifactsTarballPattern="$artifactsBaseFileName.*.tar.gz" -sharedComponentsBaseFileName="Private.SourceBuilt.SharedComponents" -sharedComponentsTarballPattern="$sharedComponentsBaseFileName.*.tar.gz" - prebuiltsBaseFileName="Private.SourceBuilt.Prebuilts" prebuiltsTarballPattern="$prebuiltsBaseFileName.*.tar.gz" @@ -127,95 +127,6 @@ while :; do shift done -# Helper to extract a property value from an XML file -function GetXmlPropertyValue { - local propName="$1" - local filePath="$2" - local value="" - local line pattern - line=$(grep -m 1 "<$propName>" "$filePath" || :) - pattern="<$propName>(.*)" - if [[ $line =~ $pattern ]]; then - value="${BASH_REMATCH[1]}" - fi - echo "$value" -} - -# Helper to download a file with retries -function DownloadWithRetries { - local url="$1" - local targetDir="$2" - ( - cd "$targetDir" && - for i in {1..5}; do - if curl -fL --retry 5 -O "$url"; then - return 0 - else - case $? in - 18) - sleep 3 - ;; - *) - return 1 - ;; - esac - fi - done - return 1 - ) -} - -function DownloadArchive { - local label="$1" - local propertyName="$2" - local isRequired="$3" - local artifactsRid="$4" - local outputDir="$5" - local destinationFilenamePrefix="${6:-}" - - local packageVersionsPath="$REPO_ROOT/eng/Versions.props" - local notFoundMessage="No $label found to download..." - - local archiveVersion - archiveVersion=$(GetXmlPropertyValue "$propertyName" "$packageVersionsPath") - if [[ -z "$archiveVersion" ]]; then - if [ "$isRequired" == true ]; then - echo " ERROR: $notFoundMessage" - exit 1 - else - echo " $notFoundMessage" - return - fi - fi - - local archiveUrl - if [[ "$propertyName" == "MicrosoftNETSdkVersion" || "$propertyName" == *Artifacts* ]]; then - archiveUrl="https://ci.dot.net/public/source-build/$artifactsBaseFileName.$archiveVersion.$artifactsRid.tar.gz" - elif [[ "$propertyName" == *Prebuilts* ]]; then - archiveUrl="https://builds.dotnet.microsoft.com/source-built-artifacts/assets/$prebuiltsBaseFileName.$archiveVersion.$defaultArtifactsRid.tar.gz" - else - echo " ERROR: Unknown archive property name: $propertyName" - exit 1 - fi - - echo " Downloading $label from $archiveUrl..." - if ! DownloadWithRetries "$archiveUrl" "$outputDir"; then - echo " ERROR: Failed to download $archiveUrl" - exit 1 - fi - - # Rename the file if a destination filename prefix is provided - if [[ -n "$destinationFilenamePrefix" ]]; then - local downloadedFilename - downloadedFilename=$(basename "$archiveUrl") - # Extract the suffix from the downloaded filename - local suffix="${downloadedFilename#$artifactsBaseFileName}" - local newFilename="$destinationFilenamePrefix$suffix" - mv "$outputDir/$downloadedFilename" "$outputDir/$newFilename" - echo " Renamed $downloadedFilename to $newFilename" - fi -} - function BootstrapArtifacts { DOTNET_SDK_PATH="$REPO_ROOT/.dotnet" local tarballDir="$1" @@ -274,13 +185,6 @@ if [ "$downloadArtifacts" == true ] && [ -f ${packagesArchiveDir}${artifactsTarb downloadPsbArtifacts=false fi -# Check if shared components archive exists -downloadSharedComponentsArtifacts=$downloadArtifacts -if [ "$downloadArtifacts" == true ] && [ -f ${packagesArchiveDir}${sharedComponentsTarballPattern} ]; then - echo " $sharedComponentsTarballPattern exists in $packagesArchiveDir...it will not be downloaded" - downloadSharedComponentsArtifacts=false -fi - # Check if Private.SourceBuilt prebuilts archive exists if [ "$downloadPrebuilts" == true ] && [ -f ${packagesArchiveDir}${prebuiltsTarballPattern} ]; then echo " $prebuiltsTarballPattern exists in $packagesArchiveDir...it will not be downloaded" @@ -313,12 +217,7 @@ if [ "$downloadPsbArtifacts" == true ]; then fi fi -if [ "$downloadSharedComponentsArtifacts" == true ]; then - DownloadArchive "shared component artifacts" "MicrosoftNETSdkVersion" false "$artifactsRid" "$packagesArchiveDir" "$sharedComponentsBaseFileName" -fi - if [ "$downloadPrebuilts" == true ]; then - DownloadArchive "prebuilts" "PrivateSourceBuiltPrebuiltsVersion" false "$artifactsRid" "$packagesArchiveDir" fi From 0bdbc7ab0617c8f970a0876143fc69413c4afbd1 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Mon, 15 Sep 2025 23:01:34 +0200 Subject: [PATCH 5/6] Create roslyn apphosts (#2425) --- .../src/CpioEntry.cs | 2 + .../SignToolTests.cs | 2 +- .../Microsoft.DotNet.SignTool/src/ZipData.cs | 40 +++++++++++++++---- .../src/ZipDataEntry.cs | 2 + src/sdk/eng/Signing.props | 3 ++ src/sdk/src/Layout/Directory.Build.props | 6 +++ .../Layout/redist/targets/Crossgen.targets | 1 + .../redist/targets/GenerateLayout.targets | 7 ++++ 8 files changed, 54 insertions(+), 9 deletions(-) diff --git a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/CpioEntry.cs b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/CpioEntry.cs index aed27483c65..19377abf9ee 100644 --- a/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/CpioEntry.cs +++ b/src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/CpioEntry.cs @@ -22,6 +22,8 @@ internal sealed class CpioEntry(ulong inode, string name, ulong timestamp, ulong public const uint Directory = 0x4000; + public const uint FilePermissionMask = 0xFFF; + public ulong Inode { get; } = inode; public string Name { get; } = name; public ulong Timestamp { get; } = timestamp; diff --git a/src/arcade/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs b/src/arcade/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs index 47bcb228381..aaab1f56e66 100644 --- a/src/arcade/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs +++ b/src/arcade/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs @@ -477,7 +477,7 @@ private void ValidateProducedRpmContent( string layout = Path.Combine(tempDir, "layout"); Directory.CreateDirectory(layout); - ZipData.ExtractRpmPayloadContents(rpmPackage, layout); + ZipData.ExtractRpmPayloadContents(log: null, rpmPackage, layout); // Checks: // Expected files are present diff --git a/src/arcade/src/Microsoft.DotNet.SignTool/src/ZipData.cs b/src/arcade/src/Microsoft.DotNet.SignTool/src/ZipData.cs index 65681e36a19..2225300b2f6 100644 --- a/src/arcade/src/Microsoft.DotNet.SignTool/src/ZipData.cs +++ b/src/arcade/src/Microsoft.DotNet.SignTool/src/ZipData.cs @@ -636,7 +636,7 @@ internal static IEnumerable ReadDebContainerEntries(string archive { string relativePath = entry.Name; // lgtm [cs/zipslip] Archive from trusted source - // The relative path ocassionally ends with a '/', which is not a valid path given that the path is a file. + // The relative path occasionally ends with a '/', which is not a valid path given that the path is a file. // Remove the following workaround once https://github.com/dotnet/arcade/issues/15384 is resolved. if (relativePath.EndsWith("/")) { @@ -658,7 +658,10 @@ private static IEnumerable ReadRpmContainerEntries(string archiveP while (archive.GetNextEntry() is CpioEntry entry) { - yield return new ZipDataEntry(entry.Name, entry.DataStream); + yield return new ZipDataEntry(entry.Name, entry.DataStream) + { + UnixFileMode = entry.Mode & CpioEntry.FilePermissionMask, + }; } } @@ -669,7 +672,7 @@ private void RepackRpmContainer(TaskLoggingHelper log, string tempDir) Directory.CreateDirectory(workingDir); string layout = Path.Combine(workingDir, "layout"); Directory.CreateDirectory(layout); - ExtractRpmPayloadContents(FileSignInfo.FullPath, layout); + ExtractRpmPayloadContents(log, FileSignInfo.FullPath, layout); // Update signed files in layout foreach (var signedPart in NestedParts.Values) @@ -680,10 +683,10 @@ private void RepackRpmContainer(TaskLoggingHelper log, string tempDir) // Create payload.cpio string payload = Path.Combine(workingDir, "payload.cpio"); - RunExternalProcess("bash", $"-c \"find . -depth ! -wholename '.' -print | cpio -H newc -o --quiet > '{payload}'\"", out string _, layout); + RunExternalProcess(log, "bash", $"-c \"find . -depth ! -wholename '.' -print | cpio -H newc -o --quiet > '{payload}'\"", out string _, layout); // Collect file types for all files in layout - RunExternalProcess("bash", $"-c \"find . -depth ! -wholename '.' -exec file {{}} \\;\"", out string output, layout); + RunExternalProcess(log, "bash", $"-c \"find . -depth ! -wholename '.' -exec file {{}} \\;\"", out string output, layout); ITaskItem[] rawPayloadFileKinds = output.Split('\n', StringSplitOptions.RemoveEmptyEntries) .Select(t => new TaskItem(t)) @@ -744,7 +747,7 @@ private void RepackRpmContainer(TaskLoggingHelper log, string tempDir) return RpmPackage.Read(stream).Header.Entries; } - internal static void ExtractRpmPayloadContents(string rpmPackage, string layout) + internal static void ExtractRpmPayloadContents(TaskLoggingHelper log, string rpmPackage, string layout) { foreach (var entry in ReadRpmContainerEntries(rpmPackage)) { @@ -754,18 +757,28 @@ internal static void ExtractRpmPayloadContents(string rpmPackage, string layout) if (entry != null) { entry.WriteToFile(outputPath); + + // Set file mode if not the default. + if (entry.UnixFileMode is { } mode and not /* 0644 */ 420) + { + RunExternalProcess(log, "bash", $""" + -c "chmod {Convert.ToString(mode, 8)} '{outputPath}'" + """, out string _, layout); + } } } } - private static bool RunExternalProcess(string cmd, string args, out string output, string workingDir = null) + private static bool RunExternalProcess(TaskLoggingHelper log, string cmd, string args, out string output, string workingDir = null) { + log?.LogMessage(MessageImportance.Low, $"Running command: '{cmd}' {args}"); + ProcessStartInfo psi = new() { FileName = cmd, Arguments = args, RedirectStandardOutput = true, - RedirectStandardError = false, + RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true, WorkingDirectory = workingDir @@ -775,6 +788,17 @@ private static bool RunExternalProcess(string cmd, string args, out string outpu output = process.StandardOutput.ReadToEnd(); process.WaitForExit(); + string stderr = process.StandardError.ReadToEnd(); + if (!string.IsNullOrWhiteSpace(stderr)) + { + log?.LogMessage(MessageImportance.Low, $" Stderr: {stderr}"); + } + + if (process.ExitCode != 0) + { + log?.LogMessage(MessageImportance.Low, $" Exit code: {process.ExitCode}"); + } + return process.ExitCode == 0; } #endif diff --git a/src/arcade/src/Microsoft.DotNet.SignTool/src/ZipDataEntry.cs b/src/arcade/src/Microsoft.DotNet.SignTool/src/ZipDataEntry.cs index e2d34d86cc1..0bdac8c92e2 100644 --- a/src/arcade/src/Microsoft.DotNet.SignTool/src/ZipDataEntry.cs +++ b/src/arcade/src/Microsoft.DotNet.SignTool/src/ZipDataEntry.cs @@ -66,6 +66,8 @@ public ZipDataEntry(ZipArchiveEntry entry) public ImmutableArray ContentHash => _contentHash; + public uint? UnixFileMode { get; set; } + public void WriteToFile(string path) { using var fs = File.Create(path); diff --git a/src/sdk/eng/Signing.props b/src/sdk/eng/Signing.props index 484697efecb..872602d12f5 100644 --- a/src/sdk/eng/Signing.props +++ b/src/sdk/eng/Signing.props @@ -84,6 +84,9 @@ + + + diff --git a/src/sdk/src/Layout/Directory.Build.props b/src/sdk/src/Layout/Directory.Build.props index 479a7c0a421..c8bc9ffafc2 100644 --- a/src/sdk/src/Layout/Directory.Build.props +++ b/src/sdk/src/Layout/Directory.Build.props @@ -78,4 +78,10 @@ $(MSBuildThisFileDirectory)pkg\ + + <_RoslynAppHost Include="$(OutputPath)Roslyn\bincore\csc.dll" /> + <_RoslynAppHost Include="$(OutputPath)Roslyn\bincore\vbc.dll" /> + <_RoslynAppHost Include="$(OutputPath)Roslyn\bincore\VBCSCompiler.dll" /> + + diff --git a/src/sdk/src/Layout/redist/targets/Crossgen.targets b/src/sdk/src/Layout/redist/targets/Crossgen.targets index 909c5de0bf3..3e07f481af1 100644 --- a/src/sdk/src/Layout/redist/targets/Crossgen.targets +++ b/src/sdk/src/Layout/redist/targets/Crossgen.targets @@ -197,6 +197,7 @@ + diff --git a/src/sdk/src/Layout/redist/targets/GenerateLayout.targets b/src/sdk/src/Layout/redist/targets/GenerateLayout.targets index 65d4aff197c..c3731494df6 100644 --- a/src/sdk/src/Layout/redist/targets/GenerateLayout.targets +++ b/src/sdk/src/Layout/redist/targets/GenerateLayout.targets @@ -59,6 +59,12 @@ + + @@ -495,6 +501,7 @@ + From b0eeb50560544fa782bd8bbddbe2e964c2ab6388 Mon Sep 17 00:00:00 2001 From: Chet Husk Date: Mon, 15 Sep 2025 18:31:43 -0500 Subject: [PATCH 6/6] Comment out global.json telemetry to prevent errant stdout writing (#2430) --- src/sdk/src/Cli/dotnet/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sdk/src/Cli/dotnet/Program.cs b/src/sdk/src/Cli/dotnet/Program.cs index cd82a15330f..ea6537b4151 100644 --- a/src/sdk/src/Cli/dotnet/Program.cs +++ b/src/sdk/src/Cli/dotnet/Program.cs @@ -237,8 +237,8 @@ internal static int ProcessArgs(string[] args, TimeSpan startupTime) // Get the global.json state to report in telemetry along with this command invocation. // We don't care about the actual SDK resolution, just the global.json information, // so just pass empty string as executable directory for resolution. - NativeWrapper.SdkResolutionResult result = NativeWrapper.NETCoreSdkResolverNativeWrapper.ResolveSdk(string.Empty, Environment.CurrentDirectory); - globalJsonState = result.GlobalJsonState; + // NativeWrapper.SdkResolutionResult result = NativeWrapper.NETCoreSdkResolverNativeWrapper.ResolveSdk(string.Empty, Environment.CurrentDirectory); + // globalJsonState = result.GlobalJsonState; } TelemetryEventEntry.SendFiltered(Tuple.Create(parseResult, performanceData, globalJsonState));